From ae69b22c6ca7d1d7fdccf0b664dafbc777099abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Ha=C5=82asa?= Date: Fri, 11 Feb 2011 13:36:27 +0100 Subject: staging: Solo6x10: Stripped "solo6010-" from file names. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver supports both Solo-6010 and Solo-6110 chips anyway. Renamed solo6010.h -> solo6x10.h. Signed-off-by: Krzysztof HaƂasa Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/solo6x10/Makefile b/drivers/staging/solo6x10/Makefile index 1616b55..72816cf 100644 --- a/drivers/staging/solo6x10/Makefile +++ b/drivers/staging/solo6x10/Makefile @@ -1,6 +1,3 @@ -solo6x10-y := solo6010-core.o solo6010-i2c.o solo6010-p2m.o \ - solo6010-v4l2.o solo6010-tw28.o solo6010-gpio.o \ - solo6010-disp.o solo6010-enc.o solo6010-v4l2-enc.o \ - solo6010-g723.o +solo6x10-y := core.o i2c.o p2m.o v4l2.o tw28.o gpio.o disp.o enc.o v4l2-enc.o g723.o obj-$(CONFIG_SOLO6X10) := solo6x10.o diff --git a/drivers/staging/solo6x10/core.c b/drivers/staging/solo6x10/core.c new file mode 100644 index 0000000..37e6fda --- /dev/null +++ b/drivers/staging/solo6x10/core.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include "solo6x10.h" +#include "tw28.h" + +MODULE_DESCRIPTION("Softlogic 6010 MP4 Encoder/Decoder V4L2/ALSA Driver"); +MODULE_AUTHOR("Ben Collins "); +MODULE_VERSION(SOLO6010_VERSION); +MODULE_LICENSE("GPL"); + +void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask) +{ + solo_dev->irq_mask |= mask; + solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); +} + +void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask) +{ + solo_dev->irq_mask &= ~mask; + solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); +} + +/* XXX We should check the return value of the sub-device ISR's */ +static irqreturn_t solo6010_isr(int irq, void *data) +{ + struct solo6010_dev *solo_dev = data; + u32 status; + int i; + + status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); + if (!status) + return IRQ_NONE; + + if (status & ~solo_dev->irq_mask) { + solo_reg_write(solo_dev, SOLO_IRQ_STAT, + status & ~solo_dev->irq_mask); + status &= solo_dev->irq_mask; + } + + if (status & SOLO_IRQ_PCI_ERR) { + u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR); + solo_p2m_error_isr(solo_dev, err); + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR); + } + + for (i = 0; i < SOLO_NR_P2M; i++) + if (status & SOLO_IRQ_P2M(i)) + solo_p2m_isr(solo_dev, i); + + if (status & SOLO_IRQ_IIC) + solo_i2c_isr(solo_dev); + + if (status & SOLO_IRQ_VIDEO_IN) + solo_video_in_isr(solo_dev); + + /* Call this first so enc gets detected flag set */ + if (status & SOLO_IRQ_MOTION) + solo_motion_isr(solo_dev); + + if (status & SOLO_IRQ_ENCODER) + solo_enc_v4l2_isr(solo_dev); + + if (status & SOLO_IRQ_G723) + solo_g723_isr(solo_dev); + + return IRQ_HANDLED; +} + +static void free_solo_dev(struct solo6010_dev *solo_dev) +{ + struct pci_dev *pdev; + + if (!solo_dev) + return; + + pdev = solo_dev->pdev; + + /* If we never initialized the PCI device, then nothing else + * below here needs cleanup */ + if (!pdev) { + kfree(solo_dev); + return; + } + + /* Bring down the sub-devices first */ + solo_g723_exit(solo_dev); + solo_enc_v4l2_exit(solo_dev); + solo_enc_exit(solo_dev); + solo_v4l2_exit(solo_dev); + solo_disp_exit(solo_dev); + solo_gpio_exit(solo_dev); + solo_p2m_exit(solo_dev); + solo_i2c_exit(solo_dev); + + /* Now cleanup the PCI device */ + if (solo_dev->reg_base) { + solo6010_irq_off(solo_dev, ~0); + pci_iounmap(pdev, solo_dev->reg_base); + free_irq(pdev->irq, solo_dev); + } + + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + kfree(solo_dev); +} + +static int __devinit solo6010_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct solo6010_dev *solo_dev; + int ret; + int sdram; + u8 chip_id; + u32 reg; + + solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); + if (solo_dev == NULL) + return -ENOMEM; + + solo_dev->pdev = pdev; + spin_lock_init(&solo_dev->reg_io_lock); + pci_set_drvdata(pdev, solo_dev); + + ret = pci_enable_device(pdev); + if (ret) + goto fail_probe; + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, SOLO6010_NAME); + if (ret) + goto fail_probe; + + solo_dev->reg_base = pci_ioremap_bar(pdev, 0); + if (solo_dev->reg_base == NULL) { + ret = -ENOMEM; + goto fail_probe; + } + + chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & + SOLO_CHIP_ID_MASK; + switch (chip_id) { + case 7: + solo_dev->nr_chans = 16; + solo_dev->nr_ext = 5; + break; + case 6: + solo_dev->nr_chans = 8; + solo_dev->nr_ext = 2; + break; + default: + dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, " + "defaulting to 4 channels\n", + chip_id); + case 5: + solo_dev->nr_chans = 4; + solo_dev->nr_ext = 1; + } + + solo_dev->flags = id->driver_data; + + /* Disable all interrupts to start */ + solo6010_irq_off(solo_dev, ~0); + + reg = SOLO_SYS_CFG_SDRAM64BIT; + /* Initial global settings */ + if (!(solo_dev->flags & FLAGS_6110)) + reg |= SOLO6010_SYS_CFG_INPUTDIV(25) | + SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) | + SOLO6010_SYS_CFG_OUTDIV(3); + solo_reg_write(solo_dev, SOLO_SYS_CFG, reg); + + if (solo_dev->flags & FLAGS_6110) { + u32 sys_clock_MHz = SOLO_CLOCK_MHZ; + u32 pll_DIVQ; + u32 pll_DIVF; + + if (sys_clock_MHz < 125) { + pll_DIVQ = 3; + pll_DIVF = (sys_clock_MHz * 4) / 3; + } else { + pll_DIVQ = 2; + pll_DIVF = (sys_clock_MHz * 2) / 3; + } + + solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG, + SOLO6110_PLL_RANGE_5_10MHZ | + SOLO6110_PLL_DIVR(9) | + SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) | + SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN); + mdelay(1); // PLL Locking time (1ms) + + solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */ + } else + solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */ + + solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1); + + /* PLL locking time of 1ms */ + mdelay(1); + + ret = request_irq(pdev->irq, solo6010_isr, IRQF_SHARED, SOLO6010_NAME, + solo_dev); + if (ret) + goto fail_probe; + + /* Handle this from the start */ + solo6010_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); + + ret = solo_i2c_init(solo_dev); + if (ret) + goto fail_probe; + + /* Setup the DMA engine */ + sdram = (solo_dev->nr_chans >= 8) ? 2 : 1; + solo_reg_write(solo_dev, SOLO_DMA_CTRL, + SOLO_DMA_CTRL_REFRESH_CYCLE(1) | + SOLO_DMA_CTRL_SDRAM_SIZE(sdram) | + SOLO_DMA_CTRL_SDRAM_CLK_INVERT | + SOLO_DMA_CTRL_READ_CLK_SELECT | + SOLO_DMA_CTRL_LATENCY(1)); + + ret = solo_p2m_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_disp_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_gpio_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_tw28_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_v4l2_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_enc_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_enc_v4l2_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_g723_init(solo_dev); + if (ret) + goto fail_probe; + + return 0; + +fail_probe: + free_solo_dev(solo_dev); + return ret; +} + +static void __devexit solo6010_pci_remove(struct pci_dev *pdev) +{ + struct solo6010_dev *solo_dev = pci_get_drvdata(pdev); + + free_solo_dev(solo_dev); +} + +static struct pci_device_id solo6010_id_table[] = { + /* 6010 based cards */ + {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)}, + {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), + .driver_data = FLAGS_6110}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)}, + /* 6110 based cards */ + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, solo6010_id_table); + +static struct pci_driver solo6010_pci_driver = { + .name = SOLO6010_NAME, + .id_table = solo6010_id_table, + .probe = solo6010_pci_probe, + .remove = solo6010_pci_remove, +}; + +static int __init solo6010_module_init(void) +{ + return pci_register_driver(&solo6010_pci_driver); +} + +static void __exit solo6010_module_exit(void) +{ + pci_unregister_driver(&solo6010_pci_driver); +} + +module_init(solo6010_module_init); +module_exit(solo6010_module_exit); diff --git a/drivers/staging/solo6x10/disp.c b/drivers/staging/solo6x10/disp.c new file mode 100644 index 0000000..1b0cfa8 --- /dev/null +++ b/drivers/staging/solo6x10/disp.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include "solo6x10.h" + +#define SOLO_VCLK_DELAY 3 +#define SOLO_PROGRESSIVE_VSIZE 1024 + +#define SOLO_MOT_THRESH_W 64 +#define SOLO_MOT_THRESH_H 64 +#define SOLO_MOT_THRESH_SIZE 8192 +#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) +#define SOLO_MOT_FLAG_SIZE 512 +#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32) + +static unsigned video_type; +module_param(video_type, uint, 0644); +MODULE_PARM_DESC(video_type, "video_type (0 = NTSC/Default, 1 = PAL)"); + +static void solo_vin_config(struct solo6010_dev *solo_dev) +{ + solo_dev->vin_hstart = 8; + solo_dev->vin_vstart = 2; + + solo_reg_write(solo_dev, SOLO_SYS_VCLK, + SOLO_VCLK_SELECT(2) | + SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, + SOLO_VI_H_START(solo_dev->vin_hstart) | + SOLO_VI_V_START(solo_dev->vin_vstart) | + SOLO_VI_V_STOP(solo_dev->vin_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, + SOLO_VI_H_START(solo_dev->vout_hstart) | + SOLO_VI_V_START(solo_dev->vout_vstart) | + SOLO_VI_V_STOP(solo_dev->vout_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_P, + SOLO_VI_H_START(0) | + SOLO_VI_V_START(1) | + SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); + + solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, + SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); + + solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); + solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, + SOLO_VI_PB_USER_MODE); + solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, + SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, + SOLO_VI_PB_VSTART(4) | + SOLO_VI_PB_VSTOP(4 + 240)); + } else { + solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, + SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); + solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, + SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, + SOLO_VI_PB_VSTART(4) | + SOLO_VI_PB_VSTOP(4 + 288)); + } + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | + SOLO_VI_PB_HSTOP(16 + 720)); +} + +static void solo_disp_config(struct solo6010_dev *solo_dev) +{ + solo_dev->vout_hstart = 6; + solo_dev->vout_vstart = 8; + + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, + (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, + (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); + solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, + (16 << 24) | (128 << 16) | (16 << 8) | 128); + + solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, + solo_dev->video_type | + SOLO_VO_USER_COLOR_SET_NAV | + SOLO_VO_NA_COLOR_Y(0) | + SOLO_VO_NA_COLOR_CB(0) | + SOLO_VO_NA_COLOR_CR(0)); + + solo_reg_write(solo_dev, SOLO_VO_ACT_H, + SOLO_VO_H_START(solo_dev->vout_hstart) | + SOLO_VO_H_STOP(solo_dev->vout_hstart + + solo_dev->video_hsize)); + + solo_reg_write(solo_dev, SOLO_VO_ACT_V, + SOLO_VO_V_START(solo_dev->vout_vstart) | + SOLO_VO_V_STOP(solo_dev->vout_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, + SOLO_VO_H_LEN(solo_dev->video_hsize) | + SOLO_VO_V_LEN(solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 5); + + solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | + SOLO_VO_DISP_ERASE_COUNT(8) | + SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); + + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + + /* Enable channels we support */ + solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1); + + /* Disable the watchdog */ + solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); +} + +static int solo_dma_vin_region(struct solo6010_dev *solo_dev, u32 off, + u16 val, int reg_size) +{ + u16 buf[64]; + int i; + int ret = 0; + + for (i = 0; i < sizeof(buf) >> 1; i++) + buf[i] = val; + + for (i = 0; i < reg_size; i += sizeof(buf)) + ret |= solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_VIN, 1, buf, + SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, + sizeof(buf)); + + return ret; +} + +void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val) +{ + if (ch > solo_dev->nr_chans) + return; + + solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + (ch * SOLO_MOT_THRESH_SIZE * 2), + val, SOLO_MOT_THRESH_REAL); +} + +/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k + * threshold and working table for each channel. Atleast that's what the + * spec says. However, this code (take from rdk) has some mystery 8k + * block right after the flag area, before the first thresh table. */ +static void solo_motion_config(struct solo6010_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + /* Clear motion flag area */ + solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, + SOLO_MOT_FLAG_SIZE); + + /* Clear working cache table */ + solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + SOLO_MOT_THRESH_SIZE + + (i * SOLO_MOT_THRESH_SIZE * 2), + 0x0000, SOLO_MOT_THRESH_REAL); + + /* Set default threshold table */ + solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); + } + + /* Default motion settings */ + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, + SOLO_VI_MOTION_FRAME_COUNT(3) | + SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) + | /* SOLO_VI_MOTION_INTR_START_STOP | */ + SOLO_VI_MOTION_SAMPLE_COUNT(10)); + + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); +} + +int solo_disp_init(struct solo6010_dev *solo_dev) +{ + int i; + + solo_dev->video_hsize = 704; + if (video_type == 0) { + solo_dev->video_type = SOLO_VO_FMT_TYPE_NTSC; + solo_dev->video_vsize = 240; + solo_dev->fps = 30; + } else { + solo_dev->video_type = SOLO_VO_FMT_TYPE_PAL; + solo_dev->video_vsize = 288; + solo_dev->fps = 25; + } + + solo_vin_config(solo_dev); + solo_motion_config(solo_dev); + solo_disp_config(solo_dev); + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); + + return 0; +} + +void solo_disp_exit(struct solo6010_dev *solo_dev) +{ + int i; + + solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION); + + solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); + solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); + } + + /* Set default border */ + for (i = 0; i < 5; i++) + solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); + + for (i = 0; i < 5; i++) + solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); + + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); + + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); + + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); +} diff --git a/drivers/staging/solo6x10/enc.c b/drivers/staging/solo6x10/enc.c new file mode 100644 index 0000000..755626c --- /dev/null +++ b/drivers/staging/solo6x10/enc.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include "solo6x10.h" +#include "osd-font.h" + +#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */ +#define OSG_BUFFER_SIZE 1024 + +#define VI_PROG_HSIZE (1280 - 16) +#define VI_PROG_VSIZE (1024 - 16) + +static void solo_capture_config(struct solo6010_dev *solo_dev) +{ + int i, j; + unsigned long height; + unsigned long width; + unsigned char *buf; + + solo_reg_write(solo_dev, SOLO_CAP_BASE, + SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE * + solo_dev->nr_chans) | + SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); + solo_reg_write(solo_dev, SOLO_CAP_BTW, + (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | + SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH)); + + /* Set scale 1, 9 dimension */ + width = solo_dev->video_hsize; + height = solo_dev->video_vsize; + solo_reg_write(solo_dev, SOLO_DIM_SCALE1, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 2, 10 dimension */ + width = solo_dev->video_hsize / 2; + height = solo_dev->video_vsize; + solo_reg_write(solo_dev, SOLO_DIM_SCALE2, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 3, 11 dimension */ + width = solo_dev->video_hsize / 2; + height = solo_dev->video_vsize / 2; + solo_reg_write(solo_dev, SOLO_DIM_SCALE3, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 4, 12 dimension */ + width = solo_dev->video_hsize / 3; + height = solo_dev->video_vsize / 3; + solo_reg_write(solo_dev, SOLO_DIM_SCALE4, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 5, 13 dimension */ + width = solo_dev->video_hsize / 4; + height = solo_dev->video_vsize / 2; + solo_reg_write(solo_dev, SOLO_DIM_SCALE5, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Progressive */ + width = VI_PROG_HSIZE; + height = VI_PROG_VSIZE; + solo_reg_write(solo_dev, SOLO_DIM_PROG, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 16) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Clear OSD */ + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, 0); + solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); + solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, + 0xF0 << 16 | 0x80 << 8 | 0x80); + solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, 0); + + /* Clear OSG buffer */ + buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return; + + for (i = 0; i < solo_dev->nr_chans; i++) { + for (j = 0; j < SOLO_EOSD_EXT_SIZE; j += OSG_BUFFER_SIZE) { + solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf, + SOLO_EOSD_EXT_ADDR + + (i * SOLO_EOSD_EXT_SIZE) + j, + OSG_BUFFER_SIZE); + } + } + kfree(buf); +} + +int solo_osd_print(struct solo_enc_dev *solo_enc) +{ + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + char *str = solo_enc->osd_text; + u8 *buf; + u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); + int len = strlen(str); + int i, j; + int x = 1, y = 1; + + if (len == 0) { + reg &= ~(1 << solo_enc->ch); + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); + return 0; + } + + buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < len; i++) { + for (j = 0; j < 16; j++) { + buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] = + (solo_osd_font[(str[i] * 4) + (j / 4)] + >> ((3 - (j % 4)) * 8)) & 0xff; + } + } + + solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR + + (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE); + reg |= (1 << solo_enc->ch); + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); + + kfree(buf); + + return 0; +} + +static void solo_jpeg_config(struct solo6010_dev *solo_dev) +{ + u32 reg; + if (solo_dev->flags & FLAGS_6110) + reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0); + else + reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0); + solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, + (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | + ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); + solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); + /* que limit, samp limit, pos limit */ + solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60); +} + +static void solo_mp4e_config(struct solo6010_dev *solo_dev) +{ + int i; + u32 reg; + + /* We can only use VE_INTR_CTRL(0) if we want to support mjpeg */ + solo_reg_write(solo_dev, SOLO_VE_CFG0, + SOLO_VE_INTR_CTRL(0) | + SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | + SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); + + solo_reg_write(solo_dev, SOLO_VE_CFG1, + SOLO_VE_INSERT_INDEX | SOLO_VE_MOTION_MODE(0)); + + solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); + solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); + solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); + solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); + solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); + + reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) | + SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15); + if (solo_dev->flags & FLAGS_6110) + reg |= SOLO_DCT_INTERVAL(10); + else + reg |= SOLO_DCT_INTERVAL(36 / 4); + solo_reg_write(solo_dev, SOLO_VE_ATTR, reg); + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), + (SOLO_EREF_EXT_ADDR(solo_dev) + + (i * SOLO_EREF_EXT_SIZE)) >> 16); + + if (solo_dev->flags & FLAGS_6110) + solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */ +} + +int solo_enc_init(struct solo6010_dev *solo_dev) +{ + int i; + + solo_capture_config(solo_dev); + solo_mp4e_config(solo_dev); + solo_jpeg_config(solo_dev); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); + } + + solo6010_irq_on(solo_dev, SOLO_IRQ_ENCODER); + + return 0; +} + +void solo_enc_exit(struct solo6010_dev *solo_dev) +{ + int i; + + solo6010_irq_off(solo_dev, SOLO_IRQ_ENCODER); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); + } +} diff --git a/drivers/staging/solo6x10/g723.c b/drivers/staging/solo6x10/g723.c new file mode 100644 index 0000000..4aba8f3 --- /dev/null +++ b/drivers/staging/solo6x10/g723.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "solo6x10.h" +#include "tw28.h" + +#define G723_INTR_ORDER 0 +#define G723_FDMA_PAGES 32 +#define G723_PERIOD_BYTES 48 +#define G723_PERIOD_BLOCK 1024 +#define G723_FRAMES_PER_PAGE 48 + +/* Sets up channels 16-19 for decoding and 0-15 for encoding */ +#define OUTMODE_MASK 0x300 + +#define SAMPLERATE 8000 +#define BITRATE 25 + +/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page + * is broken down to 20 * 48 byte regions (one for each channel possible) + * with the rest of the page being dummy data. */ +#define MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) +#define IRQ_PAGES 4 /* 0 - 4 */ +#define PERIODS_MIN (1 << IRQ_PAGES) +#define PERIODS_MAX G723_FDMA_PAGES + +struct solo_snd_pcm { + int on; + spinlock_t lock; + struct solo6010_dev *solo_dev; + unsigned char g723_buf[G723_PERIOD_BYTES]; +}; + +static void solo_g723_config(struct solo6010_dev *solo_dev) +{ + int clk_div; + + clk_div = SOLO_CLOCK_MHZ / (SAMPLERATE * (BITRATE * 2) * 2); + + solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, + SOLO_AUDIO_BITRATE(BITRATE) | + SOLO_AUDIO_CLK_DIV(clk_div)); + + solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, + SOLO_AUDIO_FDMA_INTERVAL(IRQ_PAGES) | + SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) | + SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); + + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, + SOLO_AUDIO_ENABLE | SOLO_AUDIO_I2S_MODE | + SOLO_AUDIO_I2S_MULTI(3) | SOLO_AUDIO_MODE(OUTMODE_MASK)); +} + +void solo_g723_isr(struct solo6010_dev *solo_dev) +{ + struct snd_pcm_str *pstr = + &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + struct snd_pcm_substream *ss; + struct solo_snd_pcm *solo_pcm; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723); + + for (ss = pstr->substream; ss != NULL; ss = ss->next) { + if (snd_pcm_substream_chip(ss) == NULL) + continue; + + /* This means open() hasn't been called on this one */ + if (snd_pcm_substream_chip(ss) == solo_dev) + continue; + + /* Haven't triggered a start yet */ + solo_pcm = snd_pcm_substream_chip(ss); + if (!solo_pcm->on) + continue; + + snd_pcm_period_elapsed(ss); + } +} + +static int snd_solo_hw_params(struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); +} + +static int snd_solo_hw_free(struct snd_pcm_substream *ss) +{ + return snd_pcm_lib_free_pages(ss); +} + +static struct snd_pcm_hardware snd_solo_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8, + .rates = SNDRV_PCM_RATE_8000, + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = MAX_BUFFER, + .period_bytes_min = G723_PERIOD_BYTES, + .period_bytes_max = G723_PERIOD_BYTES, + .periods_min = PERIODS_MIN, + .periods_max = PERIODS_MAX, +}; + +static int snd_solo_pcm_open(struct snd_pcm_substream *ss) +{ + struct solo6010_dev *solo_dev = snd_pcm_substream_chip(ss); + struct solo_snd_pcm *solo_pcm; + + solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); + if (solo_pcm == NULL) + return -ENOMEM; + + spin_lock_init(&solo_pcm->lock); + solo_pcm->solo_dev = solo_dev; + ss->runtime->hw = snd_solo_pcm_hw; + + snd_pcm_substream_chip(ss) = solo_pcm; + + return 0; +} + +static int snd_solo_pcm_close(struct snd_pcm_substream *ss) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + + snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; + kfree(solo_pcm); + + return 0; +} + +static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo6010_dev *solo_dev = solo_pcm->solo_dev; + int ret = 0; + + spin_lock(&solo_pcm->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (solo_pcm->on == 0) { + /* If this is the first user, switch on interrupts */ + if (atomic_inc_return(&solo_dev->snd_users) == 1) + solo6010_irq_on(solo_dev, SOLO_IRQ_G723); + solo_pcm->on = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (solo_pcm->on) { + /* If this was our last user, switch them off */ + if (atomic_dec_return(&solo_dev->snd_users) == 0) + solo6010_irq_off(solo_dev, SOLO_IRQ_G723); + solo_pcm->on = 0; + } + break; + default: + ret = -EINVAL; + } + + spin_unlock(&solo_pcm->lock); + + return ret; +} + +static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) +{ + return 0; +} + +static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo6010_dev *solo_dev = solo_pcm->solo_dev; + snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; + + return idx * G723_FRAMES_PER_PAGE; +} + +static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, + snd_pcm_uframes_t pos, void __user *dst, + snd_pcm_uframes_t count) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo6010_dev *solo_dev = solo_pcm->solo_dev; + int err, i; + + for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { + int page = (pos / G723_FRAMES_PER_PAGE) + i; + + err = solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_G723E, 0, + solo_pcm->g723_buf, + SOLO_G723_EXT_ADDR(solo_dev) + + (page * G723_PERIOD_BLOCK) + + (ss->number * G723_PERIOD_BYTES), + G723_PERIOD_BYTES); + if (err) + return err; + + err = copy_to_user(dst + (i * G723_PERIOD_BYTES), + solo_pcm->g723_buf, G723_PERIOD_BYTES); + + if (err) + return -EFAULT; + } + + return 0; +} + +static struct snd_pcm_ops snd_solo_pcm_ops = { + .open = snd_solo_pcm_open, + .close = snd_solo_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_solo_hw_params, + .hw_free = snd_solo_hw_free, + .prepare = snd_solo_pcm_prepare, + .trigger = snd_solo_pcm_trigger, + .pointer = snd_solo_pcm_pointer, + .copy = snd_solo_pcm_copy, +}; + +static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = 0; + info->value.integer.max = 15; + info->value.integer.step = 1; + + return 0; +} + +static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol); + u8 ch = value->id.numid - 1; + + value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); + + return 0; +} + +static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol); + u8 ch = value->id.numid - 1; + u8 old_val; + + old_val = tw28_get_audio_gain(solo_dev, ch); + if (old_val == value->value.integer.value[0]) + return 0; + + tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); + + return 1; +} + +static struct snd_kcontrol_new snd_solo_capture_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .info = snd_solo_capture_volume_info, + .get = snd_solo_capture_volume_get, + .put = snd_solo_capture_volume_put, +}; + +static int solo_snd_pcm_init(struct solo6010_dev *solo_dev) +{ + struct snd_card *card = solo_dev->snd_card; + struct snd_pcm *pcm; + struct snd_pcm_substream *ss; + int ret; + int i; + + ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, + &pcm); + if (ret < 0) + return ret; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_solo_pcm_ops); + + snd_pcm_chip(pcm) = solo_dev; + pcm->info_flags = 0; + strcpy(pcm->name, card->shortname); + + for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + ss; ss = ss->next, i++) + sprintf(ss->name, "Camera #%d Audio", i); + + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + MAX_BUFFER, MAX_BUFFER); + if (ret < 0) + return ret; + + solo_dev->snd_pcm = pcm; + + return 0; +} + +int solo_g723_init(struct solo6010_dev *solo_dev) +{ + static struct snd_device_ops ops = { NULL }; + struct snd_card *card; + struct snd_kcontrol_new kctl; + char name[32]; + int ret; + + atomic_set(&solo_dev->snd_users, 0); + + /* Allows for easier mapping between video and audio */ + sprintf(name, "Softlogic%d", solo_dev->vfd->num); + + ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, + &solo_dev->snd_card); + if (ret < 0) + return ret; + + card = solo_dev->snd_card; + + strcpy(card->driver, SOLO6010_NAME); + strcpy(card->shortname, "SOLO-6010 Audio"); + sprintf(card->longname, "%s on %s IRQ %d", card->shortname, + pci_name(solo_dev->pdev), solo_dev->pdev->irq); + snd_card_set_dev(card, &solo_dev->pdev->dev); + + ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); + if (ret < 0) + goto snd_error; + + /* Mixer controls */ + strcpy(card->mixername, "SOLO-6010"); + kctl = snd_solo_capture_volume; + kctl.count = solo_dev->nr_chans; + ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); + if (ret < 0) + return ret; + + ret = solo_snd_pcm_init(solo_dev); + if (ret < 0) + goto snd_error; + + ret = snd_card_register(card); + if (ret < 0) + goto snd_error; + + solo_g723_config(solo_dev); + + dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); + + return 0; + +snd_error: + snd_card_free(card); + return ret; +} + +void solo_g723_exit(struct solo6010_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); + solo6010_irq_off(solo_dev, SOLO_IRQ_G723); + + snd_card_free(solo_dev->snd_card); +} diff --git a/drivers/staging/solo6x10/gpio.c b/drivers/staging/solo6x10/gpio.c new file mode 100644 index 0000000..efd88ba --- /dev/null +++ b/drivers/staging/solo6x10/gpio.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include "solo6x10.h" + +static void solo_gpio_mode(struct solo6010_dev *solo_dev, + unsigned int port_mask, unsigned int mode) +{ + int port; + unsigned int ret; + + ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0); + + /* To set gpio */ + for (port = 0; port < 16; port++) { + if (!((1 << port) & port_mask)) + continue; + + ret &= (~(3 << (port << 1))); + ret |= ((mode & 3) << (port << 1)); + } + + solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret); + + /* To set extended gpio - sensor */ + ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1); + + for (port = 0; port < 16; port++) { + if (!((1 << (port + 16)) & port_mask)) + continue; + + if (!mode) + ret &= ~(1 << port); + else + ret |= 1 << port; + } + + solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret); +} + +static void solo_gpio_set(struct solo6010_dev *solo_dev, unsigned int value) +{ + solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, + solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value); +} + +static void solo_gpio_clear(struct solo6010_dev *solo_dev, unsigned int value) +{ + solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, + solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value); +} + +static void solo_gpio_config(struct solo6010_dev *solo_dev) +{ + /* Video reset */ + solo_gpio_mode(solo_dev, 0x30, 1); + solo_gpio_clear(solo_dev, 0x30); + udelay(100); + solo_gpio_set(solo_dev, 0x30); + udelay(100); + + /* Warning: Don't touch the next line unless you're sure of what + * you're doing: first four gpio [0-3] are used for video. */ + solo_gpio_mode(solo_dev, 0x0f, 2); + + /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */ + solo_gpio_mode(solo_dev, 0xff00, 1); + + /* Initially set relay status to 0 */ + solo_gpio_clear(solo_dev, 0xff00); +} + +int solo_gpio_init(struct solo6010_dev *solo_dev) +{ + solo_gpio_config(solo_dev); + return 0; +} + +void solo_gpio_exit(struct solo6010_dev *solo_dev) +{ + solo_gpio_clear(solo_dev, 0x30); + solo_gpio_config(solo_dev); +} diff --git a/drivers/staging/solo6x10/i2c.c b/drivers/staging/solo6x10/i2c.c new file mode 100644 index 0000000..fba4247 --- /dev/null +++ b/drivers/staging/solo6x10/i2c.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* XXX: The SOLO6010 i2c does not have separate interrupts for each i2c + * channel. The bus can only handle one i2c event at a time. The below handles + * this all wrong. We should be using the status registers to see if the bus + * is in use, and have a global lock to check the status register. Also, + * the bulk of the work should be handled out-of-interrupt. The ugly loops + * that occur during interrupt scare me. The ISR should merely signal + * thread context, ACK the interrupt, and move on. -- BenC */ + +#include +#include "solo6x10.h" + +u8 solo_i2c_readbyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off) +{ + struct i2c_msg msgs[2]; + u8 data; + + msgs[0].flags = 0; + msgs[0].addr = addr; + msgs[0].len = 1; + msgs[0].buf = &off; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = addr; + msgs[1].len = 1; + msgs[1].buf = &data; + + i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); + + return data; +} + +void solo_i2c_writebyte(struct solo6010_dev *solo_dev, int id, u8 addr, + u8 off, u8 data) +{ + struct i2c_msg msgs; + u8 buf[2]; + + buf[0] = off; + buf[1] = data; + msgs.flags = 0; + msgs.addr = addr; + msgs.len = 2; + msgs.buf = buf; + + i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); +} + +static void solo_i2c_flush(struct solo6010_dev *solo_dev, int wr) +{ + u32 ctrl; + + ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); + + if (solo_dev->i2c_state == IIC_STATE_START) + ctrl |= SOLO_IIC_START; + + if (wr) { + ctrl |= SOLO_IIC_WRITE; + } else { + ctrl |= SOLO_IIC_READ; + if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) + ctrl |= SOLO_IIC_ACK_EN; + } + + if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) + ctrl |= SOLO_IIC_STOP; + + solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); +} + +static void solo_i2c_start(struct solo6010_dev *solo_dev) +{ + u32 addr = solo_dev->i2c_msg->addr << 1; + + if (solo_dev->i2c_msg->flags & I2C_M_RD) + addr |= 1; + + solo_dev->i2c_state = IIC_STATE_START; + solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); + solo_i2c_flush(solo_dev, 1); +} + +static void solo_i2c_stop(struct solo6010_dev *solo_dev) +{ + solo6010_irq_off(solo_dev, SOLO_IRQ_IIC); + solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); + solo_dev->i2c_state = IIC_STATE_STOP; + wake_up(&solo_dev->i2c_wait); +} + +static int solo_i2c_handle_read(struct solo6010_dev *solo_dev) +{ +prepare_read: + if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { + solo_i2c_flush(solo_dev, 0); + return 0; + } + + solo_dev->i2c_msg_ptr = 0; + solo_dev->i2c_msg++; + solo_dev->i2c_msg_num--; + + if (solo_dev->i2c_msg_num == 0) { + solo_i2c_stop(solo_dev); + return 0; + } + + if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { + solo_i2c_start(solo_dev); + } else { + if (solo_dev->i2c_msg->flags & I2C_M_RD) + goto prepare_read; + else + solo_i2c_stop(solo_dev); + } + + return 0; +} + +static int solo_i2c_handle_write(struct solo6010_dev *solo_dev) +{ +retry_write: + if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { + solo_reg_write(solo_dev, SOLO_IIC_TXD, + solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); + solo_dev->i2c_msg_ptr++; + solo_i2c_flush(solo_dev, 1); + return 0; + } + + solo_dev->i2c_msg_ptr = 0; + solo_dev->i2c_msg++; + solo_dev->i2c_msg_num--; + + if (solo_dev->i2c_msg_num == 0) { + solo_i2c_stop(solo_dev); + return 0; + } + + if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { + solo_i2c_start(solo_dev); + } else { + if (solo_dev->i2c_msg->flags & I2C_M_RD) + solo_i2c_stop(solo_dev); + else + goto retry_write; + } + + return 0; +} + +int solo_i2c_isr(struct solo6010_dev *solo_dev) +{ + u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); + int ret = -EINVAL; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC); + + if (status & (SOLO_IIC_STATE_TRNS & SOLO_IIC_STATE_SIG_ERR) || + solo_dev->i2c_id < 0) { + solo_i2c_stop(solo_dev); + return -ENXIO; + } + + switch (solo_dev->i2c_state) { + case IIC_STATE_START: + if (solo_dev->i2c_msg->flags & I2C_M_RD) { + solo_dev->i2c_state = IIC_STATE_READ; + ret = solo_i2c_handle_read(solo_dev); + break; + } + + solo_dev->i2c_state = IIC_STATE_WRITE; + case IIC_STATE_WRITE: + ret = solo_i2c_handle_write(solo_dev); + break; + + case IIC_STATE_READ: + solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = + solo_reg_read(solo_dev, SOLO_IIC_RXD); + solo_dev->i2c_msg_ptr++; + + ret = solo_i2c_handle_read(solo_dev); + break; + + default: + solo_i2c_stop(solo_dev); + } + + return ret; +} + +static int solo_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct solo6010_dev *solo_dev = adap->algo_data; + unsigned long timeout; + int ret; + int i; + DEFINE_WAIT(wait); + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (&solo_dev->i2c_adap[i] == adap) + break; + } + + if (i == SOLO_I2C_ADAPTERS) + return num; /* XXX Right return value for failure? */ + + mutex_lock(&solo_dev->i2c_mutex); + solo_dev->i2c_id = i; + solo_dev->i2c_msg = msgs; + solo_dev->i2c_msg_num = num; + solo_dev->i2c_msg_ptr = 0; + + solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); + solo6010_irq_on(solo_dev, SOLO_IRQ_IIC); + solo_i2c_start(solo_dev); + + timeout = HZ / 2; + + for (;;) { + prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE); + + if (solo_dev->i2c_state == IIC_STATE_STOP) + break; + + timeout = schedule_timeout(timeout); + if (!timeout) + break; + + if (signal_pending(current)) + break; + } + + finish_wait(&solo_dev->i2c_wait, &wait); + ret = num - solo_dev->i2c_msg_num; + solo_dev->i2c_state = IIC_STATE_IDLE; + solo_dev->i2c_id = -1; + + mutex_unlock(&solo_dev->i2c_mutex); + + return ret; +} + +static u32 solo_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm solo_i2c_algo = { + .master_xfer = solo_i2c_master_xfer, + .functionality = solo_i2c_functionality, +}; + +int solo_i2c_init(struct solo6010_dev *solo_dev) +{ + int i; + int ret; + + solo_reg_write(solo_dev, SOLO_IIC_CFG, + SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); + + solo_dev->i2c_id = -1; + solo_dev->i2c_state = IIC_STATE_IDLE; + init_waitqueue_head(&solo_dev->i2c_wait); + mutex_init(&solo_dev->i2c_mutex); + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; + + snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", + SOLO6010_NAME, i); + adap->algo = &solo_i2c_algo; + adap->algo_data = solo_dev; + adap->retries = 1; + adap->dev.parent = &solo_dev->pdev->dev; + + ret = i2c_add_adapter(adap); + if (ret) { + adap->algo_data = NULL; + break; + } + } + + if (ret) { + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (!solo_dev->i2c_adap[i].algo_data) + break; + i2c_del_adapter(&solo_dev->i2c_adap[i]); + solo_dev->i2c_adap[i].algo_data = NULL; + } + return ret; + } + + dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n", + SOLO_I2C_ADAPTERS); + + return 0; +} + +void solo_i2c_exit(struct solo6010_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (!solo_dev->i2c_adap[i].algo_data) + continue; + i2c_del_adapter(&solo_dev->i2c_adap[i]); + solo_dev->i2c_adap[i].algo_data = NULL; + } +} diff --git a/drivers/staging/solo6x10/jpeg.h b/drivers/staging/solo6x10/jpeg.h new file mode 100644 index 0000000..fb0507e --- /dev/null +++ b/drivers/staging/solo6x10/jpeg.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SOLO6010_JPEG_H +#define __SOLO6010_JPEG_H + +static unsigned char jpeg_header[] = { + 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c, + 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, + 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, + 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c, + 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, + 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, 0x3a, 0x50, + 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, 0x70, 0x6e, + 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, + 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, + 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, 0xf2, 0xe0, + 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0xff, 0xdb, + 0x00, 0x43, 0x01, 0x22, 0x24, 0x24, 0x30, 0x2a, + 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6, 0x84, 0x70, + 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xff, 0xc4, 0x01, 0xa2, 0x00, + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, + 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, + 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, + 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, + 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, + 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, + 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, + 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, + 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, + 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, + 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, + 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, + 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, + 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, + 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, + 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, + 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, + 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, + 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, + 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, + 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, + 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, + 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, + 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x02, 0xc0, + 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, + 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +}; + +/* This is the byte marker for the start of SOF0: 0xffc0 marker */ +#define SOF0_START 575 + +#endif /* __SOLO6010_JPEG_H */ diff --git a/drivers/staging/solo6x10/offsets.h b/drivers/staging/solo6x10/offsets.h new file mode 100644 index 0000000..b176003 --- /dev/null +++ b/drivers/staging/solo6x10/offsets.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SOLO6010_OFFSETS_H +#define __SOLO6010_OFFSETS_H + +/* Offsets and sizes of the external address */ +#define SOLO_DISP_EXT_ADDR 0x00000000 +#define SOLO_DISP_EXT_SIZE 0x00480000 + +#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) +#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000 + +#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE) +#define SOLO_OSG_EXT_SIZE 0x00120000 + +#define SOLO_EOSD_EXT_ADDR (SOLO_OSG_EXT_ADDR + SOLO_OSG_EXT_SIZE) +#define SOLO_EOSD_EXT_SIZE 0x00010000 + +#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \ + (SOLO_EOSD_EXT_SIZE * __solo->nr_chans)) +#define SOLO_MOTION_EXT_SIZE 0x00080000 + +#define SOLO_G723_EXT_ADDR(__solo) \ + (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE) +#define SOLO_G723_EXT_SIZE 0x00010000 + +#define SOLO_CAP_EXT_ADDR(__solo) \ + (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) +#define SOLO_CAP_EXT_MAX_PAGE (18 + 15) +#define SOLO_CAP_EXT_SIZE (SOLO_CAP_EXT_MAX_PAGE * 65536) + +/* This +1 is very important -- Why?! -- BenC */ +#define SOLO_EREF_EXT_ADDR(__solo) \ + (SOLO_CAP_EXT_ADDR(__solo) + \ + (SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1))) +#define SOLO_EREF_EXT_SIZE 0x00140000 + +#define SOLO_MP4E_EXT_ADDR(__solo) \ + (SOLO_EREF_EXT_ADDR(__solo) + \ + (SOLO_EREF_EXT_SIZE * __solo->nr_chans)) +#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) + +#define SOLO_DREF_EXT_ADDR(__solo) \ + (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) +#define SOLO_DREF_EXT_SIZE 0x00140000 + +#define SOLO_MP4D_EXT_ADDR(__solo) \ + (SOLO_DREF_EXT_ADDR(__solo) + \ + (SOLO_DREF_EXT_SIZE * __solo->nr_chans)) +#define SOLO_MP4D_EXT_SIZE 0x00080000 + +#define SOLO_JPEG_EXT_ADDR(__solo) \ + (SOLO_MP4D_EXT_ADDR(__solo) + \ + (SOLO_MP4D_EXT_SIZE * __solo->nr_chans)) +#define SOLO_JPEG_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) + +#endif /* __SOLO6010_OFFSETS_H */ diff --git a/drivers/staging/solo6x10/osd-font.h b/drivers/staging/solo6x10/osd-font.h new file mode 100644 index 0000000..d72efbb --- /dev/null +++ b/drivers/staging/solo6x10/osd-font.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SOLO6010_OSD_FONT_H +#define __SOLO6010_OSD_FONT_H + +static const unsigned int solo_osd_font[] = { + 0x00000000, 0x0000c0c8, 0xccfefe0c, 0x08000000, + 0x00000000, 0x10103838, 0x7c7cfefe, 0x00000000, /* 0 */ + 0x00000000, 0xfefe7c7c, 0x38381010, 0x10000000, + 0x00000000, 0x7c82fefe, 0xfefefe7c, 0x00000000, + 0x00000000, 0x00001038, 0x10000000, 0x00000000, + 0x00000000, 0x0010387c, 0xfe7c3810, 0x00000000, + 0x00000000, 0x00384444, 0x44380000, 0x00000000, + 0x00000000, 0x38448282, 0x82443800, 0x00000000, + 0x00000000, 0x007c7c7c, 0x7c7c0000, 0x00000000, + 0x00000000, 0x6c6c6c6c, 0x6c6c6c6c, 0x00000000, + 0x00000000, 0x061e7efe, 0xfe7e1e06, 0x00000000, + 0x00000000, 0xc0f0fcfe, 0xfefcf0c0, 0x00000000, + 0x00000000, 0xc6cedefe, 0xfedecec6, 0x00000000, + 0x00000000, 0xc6e6f6fe, 0xfef6e6c6, 0x00000000, + 0x00000000, 0x12367efe, 0xfe7e3612, 0x00000000, + 0x00000000, 0x90d8fcfe, 0xfefcd890, 0x00000000, + 0x00000038, 0x7cc692ba, 0x92c67c38, 0x00000000, + 0x00000038, 0x7cc6aa92, 0xaac67c38, 0x00000000, + 0x00000038, 0x7830107c, 0xbaa8680c, 0x00000000, + 0x00000038, 0x3c18127c, 0xb8382c60, 0x00000000, + 0x00000044, 0xaa6c8254, 0x38eec67c, 0x00000000, + 0x00000082, 0x44288244, 0x38c6827c, 0x00000000, + 0x00000038, 0x444444fe, 0xfeeec6fe, 0x00000000, + 0x00000018, 0x78187818, 0x3c7e7e3c, 0x00000000, + 0x00000000, 0x3854929a, 0x82443800, 0x00000000, + 0x00000000, 0x00c0c8cc, 0xfefe0c08, 0x00000000, + 0x0000e0a0, 0xe040e00e, 0x8a0ea40e, 0x00000000, + 0x0000e0a0, 0xe040e00e, 0x0a8e440e, 0x00000000, + 0x0000007c, 0x82829292, 0x929282fe, 0x00000000, + 0x000000f8, 0xfc046494, 0x946404fc, 0x00000000, + 0x0000003f, 0x7f404c52, 0x524c407f, 0x00000000, + 0x0000007c, 0x82ba82ba, 0x82ba82fe, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x183c3c3c, 0x18180018, 0x18000000, /* 32 ! */ + 0x00000066, 0x66240000, 0x00000000, 0x00000000, + 0x00000000, 0x6c6cfe6c, 0x6c6cfe6c, 0x6c000000, /* 34 " # */ + 0x00001010, 0x7cd6d616, 0x7cd0d6d6, 0x7c101000, + 0x00000000, 0x0086c660, 0x30180cc6, 0xc2000000, /* 36 $ % */ + 0x00000000, 0x386c6c38, 0xdc766666, 0xdc000000, + 0x0000000c, 0x0c0c0600, 0x00000000, 0x00000000, /* 38 & ' */ + 0x00000000, 0x30180c0c, 0x0c0c0c18, 0x30000000, + 0x00000000, 0x0c183030, 0x30303018, 0x0c000000, /* 40 ( ) */ + 0x00000000, 0x0000663c, 0xff3c6600, 0x00000000, + 0x00000000, 0x00001818, 0x7e181800, 0x00000000, /* 42 * + */ + 0x00000000, 0x00000000, 0x00000e0e, 0x0c060000, + 0x00000000, 0x00000000, 0x7e000000, 0x00000000, /* 44 , - */ + 0x00000000, 0x00000000, 0x00000006, 0x06000000, + 0x00000000, 0x80c06030, 0x180c0602, 0x00000000, /* 46 . / */ + 0x0000007c, 0xc6e6f6de, 0xcec6c67c, 0x00000000, + 0x00000030, 0x383c3030, 0x303030fc, 0x00000000, /* 48 0 1 */ + 0x0000007c, 0xc6c06030, 0x180cc6fe, 0x00000000, + 0x0000007c, 0xc6c0c07c, 0xc0c0c67c, 0x00000000, /* 50 2 3 */ + 0x00000060, 0x70786c66, 0xfe6060f0, 0x00000000, + 0x000000fe, 0x0606067e, 0xc0c0c67c, 0x00000000, /* 52 4 5 */ + 0x00000038, 0x0c06067e, 0xc6c6c67c, 0x00000000, + 0x000000fe, 0xc6c06030, 0x18181818, 0x00000000, /* 54 6 7 */ + 0x0000007c, 0xc6c6c67c, 0xc6c6c67c, 0x00000000, + 0x0000007c, 0xc6c6c6fc, 0xc0c06038, 0x00000000, /* 56 8 9 */ + 0x00000000, 0x18180000, 0x00181800, 0x00000000, + 0x00000000, 0x18180000, 0x0018180c, 0x00000000, /* 58 : ; */ + 0x00000060, 0x30180c06, 0x0c183060, 0x00000000, + 0x00000000, 0x007e0000, 0x007e0000, 0x00000000, + 0x00000006, 0x0c183060, 0x30180c06, 0x00000000, + 0x0000007c, 0xc6c66030, 0x30003030, 0x00000000, + 0x0000007c, 0xc6f6d6d6, 0x7606067c, 0x00000000, + 0x00000010, 0x386cc6c6, 0xfec6c6c6, 0x00000000, /* 64 @ A */ + 0x0000007e, 0xc6c6c67e, 0xc6c6c67e, 0x00000000, + 0x00000078, 0xcc060606, 0x0606cc78, 0x00000000, /* 66 */ + 0x0000003e, 0x66c6c6c6, 0xc6c6663e, 0x00000000, + 0x000000fe, 0x0606063e, 0x060606fe, 0x00000000, /* 68 */ + 0x000000fe, 0x0606063e, 0x06060606, 0x00000000, + 0x00000078, 0xcc060606, 0xf6c6ccb8, 0x00000000, /* 70 */ + 0x000000c6, 0xc6c6c6fe, 0xc6c6c6c6, 0x00000000, + 0x0000003c, 0x18181818, 0x1818183c, 0x00000000, /* 72 */ + 0x00000060, 0x60606060, 0x6066663c, 0x00000000, + 0x000000c6, 0xc666361e, 0x3666c6c6, 0x00000000, /* 74 */ + 0x00000006, 0x06060606, 0x060606fe, 0x00000000, + 0x000000c6, 0xeefed6c6, 0xc6c6c6c6, 0x00000000, /* 76 */ + 0x000000c6, 0xcedefef6, 0xe6c6c6c6, 0x00000000, + 0x00000038, 0x6cc6c6c6, 0xc6c66c38, 0x00000000, /* 78 */ + 0x0000007e, 0xc6c6c67e, 0x06060606, 0x00000000, + 0x00000038, 0x6cc6c6c6, 0xc6d67c38, 0x60000000, /* 80 */ + 0x0000007e, 0xc6c6c67e, 0x66c6c6c6, 0x00000000, + 0x0000007c, 0xc6c60c38, 0x60c6c67c, 0x00000000, /* 82 */ + 0x0000007e, 0x18181818, 0x18181818, 0x00000000, + 0x000000c6, 0xc6c6c6c6, 0xc6c6c67c, 0x00000000, /* 84 */ + 0x000000c6, 0xc6c6c6c6, 0xc66c3810, 0x00000000, + 0x000000c6, 0xc6c6c6c6, 0xd6d6fe6c, 0x00000000, /* 86 */ + 0x000000c6, 0xc6c66c38, 0x6cc6c6c6, 0x00000000, + 0x00000066, 0x66666666, 0x3c181818, 0x00000000, /* 88 */ + 0x000000fe, 0xc0603018, 0x0c0606fe, 0x00000000, + 0x0000003c, 0x0c0c0c0c, 0x0c0c0c3c, 0x00000000, /* 90 */ + 0x00000002, 0x060c1830, 0x60c08000, 0x00000000, + 0x0000003c, 0x30303030, 0x3030303c, 0x00000000, /* 92 */ + 0x00001038, 0x6cc60000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00fe0000, + 0x00001818, 0x30000000, 0x00000000, 0x00000000, + 0x00000000, 0x00003c60, 0x7c66667c, 0x00000000, + 0x0000000c, 0x0c0c7ccc, 0xcccccc7c, 0x00000000, + 0x00000000, 0x00007cc6, 0x0606c67c, 0x00000000, + 0x00000060, 0x60607c66, 0x6666667c, 0x00000000, + 0x00000000, 0x00007cc6, 0xfe06c67c, 0x00000000, + 0x00000078, 0x0c0c0c3e, 0x0c0c0c0c, 0x00000000, + 0x00000000, 0x00007c66, 0x6666667c, 0x60603e00, + 0x0000000c, 0x0c0c7ccc, 0xcccccccc, 0x00000000, + 0x00000030, 0x30003830, 0x30303078, 0x00000000, + 0x00000030, 0x30003c30, 0x30303030, 0x30301f00, + 0x0000000c, 0x0c0ccc6c, 0x3c6ccccc, 0x00000000, + 0x00000030, 0x30303030, 0x30303030, 0x00000000, + 0x00000000, 0x000066fe, 0xd6d6d6d6, 0x00000000, + 0x00000000, 0x000078cc, 0xcccccccc, 0x00000000, + 0x00000000, 0x00007cc6, 0xc6c6c67c, 0x00000000, + 0x00000000, 0x00007ccc, 0xcccccc7c, 0x0c0c0c00, + 0x00000000, 0x00007c66, 0x6666667c, 0x60606000, + 0x00000000, 0x000076dc, 0x0c0c0c0c, 0x00000000, + 0x00000000, 0x00007cc6, 0x1c70c67c, 0x00000000, + 0x00000000, 0x1818fe18, 0x18181870, 0x00000000, + 0x00000000, 0x00006666, 0x6666663c, 0x00000000, + 0x00000000, 0x0000c6c6, 0xc66c3810, 0x00000000, + 0x00000000, 0x0000c6d6, 0xd6d6fe6c, 0x00000000, + 0x00000000, 0x0000c66c, 0x38386cc6, 0x00000000, + 0x00000000, 0x00006666, 0x6666667c, 0x60603e00, + 0x00000000, 0x0000fe60, 0x30180cfe, 0x00000000, + 0x00000070, 0x1818180e, 0x18181870, 0x00000000, + 0x00000018, 0x18181800, 0x18181818, 0x00000000, + 0x0000000e, 0x18181870, 0x1818180e, 0x00000000, + 0x000000dc, 0x76000000, 0x00000000, 0x00000000, + 0x00000000, 0x0010386c, 0xc6c6fe00, 0x00000000 +}; + +#endif /* __SOLO6010_OSD_FONT_H */ diff --git a/drivers/staging/solo6x10/p2m.c b/drivers/staging/solo6x10/p2m.c new file mode 100644 index 0000000..1b88d10 --- /dev/null +++ b/drivers/staging/solo6x10/p2m.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "solo6x10.h" + +/* #define SOLO_TEST_P2M */ + +int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, + void *sys_addr, u32 ext_addr, u32 size) +{ + dma_addr_t dma_addr; + int ret; + + WARN_ON(!size); + BUG_ON(id >= SOLO_NR_P2M); + + if (!size) + return -EINVAL; + + dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size); + + pci_unmap_single(solo_dev->pdev, dma_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + return ret; +} + +int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size) +{ + struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA); + int ret; + + if (desc == NULL) + return -ENOMEM; + + solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0); + ret = solo_p2m_dma_desc(solo_dev, id, desc, 2); + kfree(desc); + + return ret; +} + +void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, + u32 ext_addr, u32 size, int repeat, u32 ext_size) +{ + desc->ta = cpu_to_le32(dma_addr); + desc->fa = cpu_to_le32(ext_addr); + + desc->ext = cpu_to_le32(SOLO_P2M_COPY_SIZE(size >> 2)); + desc->ctrl = cpu_to_le32(SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | + (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON); + + /* Ext size only matters when we're repeating */ + if (repeat) { + desc->ext |= cpu_to_le32(SOLO_P2M_EXT_INC(ext_size >> 2)); + desc->ctrl |= cpu_to_le32(SOLO_P2M_PCI_INC(size >> 2) | + SOLO_P2M_REPEAT(repeat)); + } +} + +int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id, + struct p2m_desc *desc, int desc_count) +{ + struct solo_p2m_dev *p2m_dev; + unsigned int timeout; + int ret = 0; + u32 config = 0; + dma_addr_t desc_dma = 0; + + BUG_ON(id >= SOLO_NR_P2M); + BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC); + + p2m_dev = &solo_dev->p2m_dev[id]; + + mutex_lock(&p2m_dev->mutex); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); + + INIT_COMPLETION(p2m_dev->completion); + p2m_dev->error = 0; + + /* Enable the descriptors */ + config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id)); + desc_dma = pci_map_single(solo_dev->pdev, desc, + desc_count * sizeof(*desc), + PCI_DMA_TODEVICE); + solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma); + solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config | + SOLO_P2M_DESC_MODE); + + /* Should have all descriptors completed from one interrupt */ + timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); + + /* Reset back to non-descriptor mode */ + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config); + solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0); + solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0); + pci_unmap_single(solo_dev->pdev, desc_dma, + desc_count * sizeof(*desc), + PCI_DMA_TODEVICE); + + if (p2m_dev->error) + ret = -EIO; + else if (timeout == 0) + ret = -EAGAIN; + + mutex_unlock(&p2m_dev->mutex); + + WARN_ON_ONCE(ret); + + return ret; +} + +int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id, + struct p2m_desc *pdesc, int wr, + struct scatterlist *sg, u32 sg_off, + u32 ext_addr, u32 size) +{ + int i; + int idx; + + BUG_ON(id >= SOLO_NR_P2M); + + if (WARN_ON_ONCE(!size)) + return -EINVAL; + + memset(pdesc, 0, sizeof(*pdesc)); + + /* Should rewrite this to handle > SOLO_NR_P2M_DESC transactions */ + for (i = 0, idx = 1; idx < SOLO_NR_P2M_DESC && sg && size > 0; + i++, sg = sg_next(sg)) { + struct p2m_desc *desc = &pdesc[idx]; + u32 sg_len = sg_dma_len(sg); + u32 len; + + if (sg_off >= sg_len) { + sg_off -= sg_len; + continue; + } + + sg_len -= sg_off; + len = min(sg_len, size); + + solo_p2m_push_desc(desc, wr, sg_dma_address(sg) + sg_off, + ext_addr, len, 0, 0); + + size -= len; + ext_addr += len; + idx++; + + sg_off = 0; + } + + WARN_ON_ONCE(size || i >= SOLO_NR_P2M_DESC); + + return solo_p2m_dma_desc(solo_dev, id, pdesc, idx); +} + +#ifdef SOLO_TEST_P2M + +#define P2M_TEST_CHAR 0xbe + +static unsigned long long p2m_test(struct solo6010_dev *solo_dev, u8 id, + u32 base, int size) +{ + u8 *wr_buf; + u8 *rd_buf; + int i; + unsigned long long err_cnt = 0; + + wr_buf = kmalloc(size, GFP_KERNEL); + if (!wr_buf) { + printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n"); + return size; + } + + rd_buf = kmalloc(size, GFP_KERNEL); + if (!rd_buf) { + printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n"); + kfree(wr_buf); + return size; + } + + memset(wr_buf, P2M_TEST_CHAR, size); + memset(rd_buf, P2M_TEST_CHAR + 1, size); + + solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size); + solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size); + + for (i = 0; i < size; i++) + if (wr_buf[i] != rd_buf[i]) + err_cnt++; + + kfree(wr_buf); + kfree(rd_buf); + + return err_cnt; +} + +#define TEST_CHUNK_SIZE (8 * 1024) + +static void run_p2m_test(struct solo6010_dev *solo_dev) +{ + unsigned long long errs = 0; + u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); + int i, d; + + printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", + SOLO6010_NAME, size); + + for (i = 0; i < size; i += TEST_CHUNK_SIZE) + for (d = 0; d < 4; d++) + errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); + + printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", + SOLO6010_NAME, errs); + + return; +} +#else +#define run_p2m_test(__solo) do {} while (0) +#endif + +void solo_p2m_isr(struct solo6010_dev *solo_dev, int id) +{ + struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id)); + + complete(&p2m_dev->completion); +} + +void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status) +{ + struct solo_p2m_dev *p2m_dev; + int i; + + if (!(status & SOLO_PCI_ERR_P2M)) + return; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + p2m_dev->error = 1; + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + complete(&p2m_dev->completion); + } +} + +void solo_p2m_exit(struct solo6010_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) + solo6010_irq_off(solo_dev, SOLO_IRQ_P2M(i)); +} + +int solo_p2m_init(struct solo6010_dev *solo_dev) +{ + struct solo_p2m_dev *p2m_dev; + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + + mutex_init(&p2m_dev->mutex); + init_completion(&p2m_dev->completion); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), + SOLO_P2M_CSC_16BIT_565 | + SOLO_P2M_DMA_INTERVAL(3) | + SOLO_P2M_DESC_INTR_OPT | + SOLO_P2M_PCI_MASTER_MODE); + solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i)); + } + + run_p2m_test(solo_dev); + + return 0; +} diff --git a/drivers/staging/solo6x10/registers.h b/drivers/staging/solo6x10/registers.h new file mode 100644 index 0000000..edf77e8 --- /dev/null +++ b/drivers/staging/solo6x10/registers.h @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SOLO6010_REGISTERS_H +#define __SOLO6010_REGISTERS_H + +#include "offsets.h" + +/* Global 6010 system configuration */ +#define SOLO_SYS_CFG 0x0000 +#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */ +#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */ +#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */ +#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */ +#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */ +#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */ +#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000 +#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24) +#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26) +#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 /* 6110: must be set */ +#define SOLO_SYS_CFG_RESET 0x80000000 + +#define SOLO_DMA_CTRL 0x0004 +#define SOLO_DMA_CTRL_REFRESH_CYCLE(n) ((n)<<8) +/* 0=16/32MB, 1=32/64MB, 2=64/128MB, 3=128/256MB */ +#define SOLO_DMA_CTRL_SDRAM_SIZE(n) ((n)<<6) +#define SOLO_DMA_CTRL_SDRAM_CLK_INVERT (1<<5) +#define SOLO_DMA_CTRL_STROBE_SELECT (1<<4) +#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3) +#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2) +#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0) +#define SOLO_DMA_CTRL1 0x0008 + +#define SOLO_SYS_VCLK 0x000C +#define SOLO_VCLK_INVERT (1<<22) +/* 0=sys_clk/4, 1=sys_clk/2, 2=clk_in/2 of system input */ +#define SOLO_VCLK_SELECT(n) ((n)<<20) +#define SOLO_VCLK_VIN1415_DELAY(n) ((n)<<14) +#define SOLO_VCLK_VIN1213_DELAY(n) ((n)<<12) +#define SOLO_VCLK_VIN1011_DELAY(n) ((n)<<10) +#define SOLO_VCLK_VIN0809_DELAY(n) ((n)<<8) +#define SOLO_VCLK_VIN0607_DELAY(n) ((n)<<6) +#define SOLO_VCLK_VIN0405_DELAY(n) ((n)<<4) +#define SOLO_VCLK_VIN0203_DELAY(n) ((n)<<2) +#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0) + +#define SOLO_IRQ_STAT 0x0010 +#define SOLO_IRQ_ENABLE 0x0014 +#define SOLO_IRQ_P2M(n) (1<<((n)+17)) +#define SOLO_IRQ_GPIO (1<<16) +#define SOLO_IRQ_VIDEO_LOSS (1<<15) +#define SOLO_IRQ_VIDEO_IN (1<<14) +#define SOLO_IRQ_MOTION (1<<13) +#define SOLO_IRQ_ATA_CMD (1<<12) +#define SOLO_IRQ_ATA_DIR (1<<11) +#define SOLO_IRQ_PCI_ERR (1<<10) +#define SOLO_IRQ_PS2_1 (1<<9) +#define SOLO_IRQ_PS2_0 (1<<8) +#define SOLO_IRQ_SPI (1<<7) +#define SOLO_IRQ_IIC (1<<6) +#define SOLO_IRQ_UART(n) (1<<((n) + 4)) +#define SOLO_IRQ_G723 (1<<3) +#define SOLO_IRQ_DECODER (1<<1) +#define SOLO_IRQ_ENCODER (1<<0) + +#define SOLO_CHIP_OPTION 0x001C +#define SOLO_CHIP_ID_MASK 0x00000007 + +#define SOLO6110_PLL_CONFIG 0x0020 +#define SOLO6110_PLL_RANGE_BYPASS (0 << 20) +#define SOLO6110_PLL_RANGE_5_10MHZ (1 << 20) +#define SOLO6110_PLL_RANGE_8_16MHZ (2 << 20) +#define SOLO6110_PLL_RANGE_13_26MHZ (3 << 20) +#define SOLO6110_PLL_RANGE_21_42MHZ (4 << 20) +#define SOLO6110_PLL_RANGE_34_68MHZ (5 << 20) +#define SOLO6110_PLL_RANGE_54_108MHZ (6 << 20) +#define SOLO6110_PLL_RANGE_88_200MHZ (7 << 20) +#define SOLO6110_PLL_DIVR(x) (((x) - 1) << 15) +#define SOLO6110_PLL_DIVQ_EXP(x) ((x) << 12) +#define SOLO6110_PLL_DIVF(x) (((x) - 1) << 4) +#define SOLO6110_PLL_RESET (1 << 3) +#define SOLO6110_PLL_BYPASS (1 << 2) +#define SOLO6110_PLL_FSEN (1 << 1) +#define SOLO6110_PLL_FB (1 << 0) + +#define SOLO_EEPROM_CTRL 0x0060 +#define SOLO_EEPROM_ACCESS_EN (1<<7) +#define SOLO_EEPROM_CS (1<<3) +#define SOLO_EEPROM_CLK (1<<2) +#define SOLO_EEPROM_DO (1<<1) +#define SOLO_EEPROM_DI (1<<0) +#define SOLO_EEPROM_ENABLE (EEPROM_ACCESS_EN | EEPROM_CS) + +#define SOLO_PCI_ERR 0x0070 +#define SOLO_PCI_ERR_FATAL 0x00000001 +#define SOLO_PCI_ERR_PARITY 0x00000002 +#define SOLO_PCI_ERR_TARGET 0x00000004 +#define SOLO_PCI_ERR_TIMEOUT 0x00000008 +#define SOLO_PCI_ERR_P2M 0x00000010 +#define SOLO_PCI_ERR_ATA 0x00000020 +#define SOLO_PCI_ERR_P2M_DESC 0x00000040 +#define SOLO_PCI_ERR_FSM0(__s) (((__s) >> 16) & 0x0f) +#define SOLO_PCI_ERR_FSM1(__s) (((__s) >> 20) & 0x0f) +#define SOLO_PCI_ERR_FSM2(__s) (((__s) >> 24) & 0x1f) + +#define SOLO_P2M_BASE 0x0080 + +#define SOLO_P2M_CONFIG(n) (0x0080 + ((n)*0x20)) +#define SOLO_P2M_DMA_INTERVAL(n) ((n)<<6)/* N*32 clocks */ +#define SOLO_P2M_CSC_BYTE_REORDER (1<<5) /* BGR -> RGB */ +/* 0:r=[14:10] g=[9:5] b=[4:0], 1:r=[15:11] g=[10:5] b=[4:0] */ +#define SOLO_P2M_CSC_16BIT_565 (1<<4) +#define SOLO_P2M_UV_SWAP (1<<3) +#define SOLO_P2M_PCI_MASTER_MODE (1<<2) +#define SOLO_P2M_DESC_INTR_OPT (1<<1) /* 1:Empty, 0:Each */ +#define SOLO_P2M_DESC_MODE (1<<0) + +#define SOLO_P2M_DES_ADR(n) (0x0084 + ((n)*0x20)) + +#define SOLO_P2M_DESC_ID(n) (0x0088 + ((n)*0x20)) +#define SOLO_P2M_UPDATE_ID(n) ((n)<<0) + +#define SOLO_P2M_STATUS(n) (0x008C + ((n)*0x20)) +#define SOLO_P2M_COMMAND_DONE (1<<8) +#define SOLO_P2M_CURRENT_ID(stat) (0xff & (stat)) + +#define SOLO_P2M_CONTROL(n) (0x0090 + ((n)*0x20)) +#define SOLO_P2M_PCI_INC(n) ((n)<<20) +#define SOLO_P2M_REPEAT(n) ((n)<<10) +/* 0:512, 1:256, 2:128, 3:64, 4:32, 5:128(2page) */ +#define SOLO_P2M_BURST_SIZE(n) ((n)<<7) +#define SOLO_P2M_BURST_512 0 +#define SOLO_P2M_BURST_256 1 +#define SOLO_P2M_BURST_128 2 +#define SOLO_P2M_BURST_64 3 +#define SOLO_P2M_BURST_32 4 +#define SOLO_P2M_CSC_16BIT (1<<6) /* 0:24bit, 1:16bit */ +/* 0:Y[0]<-0(OFF), 1:Y[0]<-1(ON), 2:Y[0]<-G[0], 3:Y[0]<-Bit[15] */ +#define SOLO_P2M_ALPHA_MODE(n) ((n)<<4) +#define SOLO_P2M_CSC_ON (1<<3) +#define SOLO_P2M_INTERRUPT_REQ (1<<2) +#define SOLO_P2M_WRITE (1<<1) +#define SOLO_P2M_TRANS_ON (1<<0) + +#define SOLO_P2M_EXT_CFG(n) (0x0094 + ((n)*0x20)) +#define SOLO_P2M_EXT_INC(n) ((n)<<20) +#define SOLO_P2M_COPY_SIZE(n) ((n)<<0) + +#define SOLO_P2M_TAR_ADR(n) (0x0098 + ((n)*0x20)) + +#define SOLO_P2M_EXT_ADR(n) (0x009C + ((n)*0x20)) + +#define SOLO_P2M_BUFFER(i) (0x2000 + ((i)*4)) + +#define SOLO_VI_CH_SWITCH_0 0x0100 +#define SOLO_VI_CH_SWITCH_1 0x0104 +#define SOLO_VI_CH_SWITCH_2 0x0108 + +#define SOLO_VI_CH_ENA 0x010C +#define SOLO_VI_CH_FORMAT 0x0110 +#define SOLO_VI_FD_SEL_MASK(n) ((n)<<16) +#define SOLO_VI_PROG_MASK(n) ((n)<<0) + +#define SOLO_VI_FMT_CFG 0x0114 +#define SOLO_VI_FMT_CHECK_VCOUNT (1<<31) +#define SOLO_VI_FMT_CHECK_HCOUNT (1<<30) +#define SOLO_VI_FMT_TEST_SIGNAL (1<<28) + +#define SOLO_VI_PAGE_SW 0x0118 +#define SOLO_FI_INV_DISP_LIVE(n) ((n)<<8) +#define SOLO_FI_INV_DISP_OUT(n) ((n)<<7) +#define SOLO_DISP_SYNC_FI(n) ((n)<<6) +#define SOLO_PIP_PAGE_ADD(n) ((n)<<3) +#define SOLO_NORMAL_PAGE_ADD(n) ((n)<<0) + +#define SOLO_VI_ACT_I_P 0x011C +#define SOLO_VI_ACT_I_S 0x0120 +#define SOLO_VI_ACT_P 0x0124 +#define SOLO_VI_FI_INVERT (1<<31) +#define SOLO_VI_H_START(n) ((n)<<21) +#define SOLO_VI_V_START(n) ((n)<<11) +#define SOLO_VI_V_STOP(n) ((n)<<0) + +#define SOLO_VI_STATUS0 0x0128 +#define SOLO_VI_STATUS0_PAGE(__n) ((__n) & 0x07) +#define SOLO_VI_STATUS1 0x012C + +/* XXX: Might be better off in kernel level disp.h */ +#define DISP_PAGE(stat) ((stat) & 0x07) + +#define SOLO_VI_PB_CONFIG 0x0130 +#define SOLO_VI_PB_USER_MODE (1<<1) +#define SOLO_VI_PB_PAL (1<<0) +#define SOLO_VI_PB_RANGE_HV 0x0134 +#define SOLO_VI_PB_HSIZE(h) ((h)<<12) +#define SOLO_VI_PB_VSIZE(v) ((v)<<0) +#define SOLO_VI_PB_ACT_H 0x0138 +#define SOLO_VI_PB_HSTART(n) ((n)<<12) +#define SOLO_VI_PB_HSTOP(n) ((n)<<0) +#define SOLO_VI_PB_ACT_V 0x013C +#define SOLO_VI_PB_VSTART(n) ((n)<<12) +#define SOLO_VI_PB_VSTOP(n) ((n)<<0) + +#define SOLO_VI_MOSAIC(ch) (0x0140 + ((ch)*4)) +#define SOLO_VI_MOSAIC_SX(x) ((x)<<24) +#define SOLO_VI_MOSAIC_EX(x) ((x)<<16) +#define SOLO_VI_MOSAIC_SY(x) ((x)<<8) +#define SOLO_VI_MOSAIC_EY(x) ((x)<<0) + +#define SOLO_VI_WIN_CTRL0(ch) (0x0180 + ((ch)*4)) +#define SOLO_VI_WIN_CTRL1(ch) (0x01C0 + ((ch)*4)) + +#define SOLO_VI_WIN_CHANNEL(n) ((n)<<28) + +#define SOLO_VI_WIN_PIP(n) ((n)<<27) +#define SOLO_VI_WIN_SCALE(n) ((n)<<24) + +#define SOLO_VI_WIN_SX(x) ((x)<<12) +#define SOLO_VI_WIN_EX(x) ((x)<<0) + +#define SOLO_VI_WIN_SY(x) ((x)<<12) +#define SOLO_VI_WIN_EY(x) ((x)<<0) + +#define SOLO_VI_WIN_ON(ch) (0x0200 + ((ch)*4)) + +#define SOLO_VI_WIN_SW 0x0240 +#define SOLO_VI_WIN_LIVE_AUTO_MUTE 0x0244 + +#define SOLO_VI_MOT_ADR 0x0260 +#define SOLO_VI_MOTION_EN(mask) ((mask)<<16) +#define SOLO_VI_MOT_CTRL 0x0264 +#define SOLO_VI_MOTION_FRAME_COUNT(n) ((n)<<24) +#define SOLO_VI_MOTION_SAMPLE_LENGTH(n) ((n)<<16) +#define SOLO_VI_MOTION_INTR_START_STOP (1<<15) +#define SOLO_VI_MOTION_FREEZE_DATA (1<<14) +#define SOLO_VI_MOTION_SAMPLE_COUNT(n) ((n)<<0) +#define SOLO_VI_MOT_CLEAR 0x0268 +#define SOLO_VI_MOT_STATUS 0x026C +#define SOLO_VI_MOTION_CNT(n) ((n)<<0) +#define SOLO_VI_MOTION_BORDER 0x0270 +#define SOLO_VI_MOTION_BAR 0x0274 +#define SOLO_VI_MOTION_Y_SET (1<<29) +#define SOLO_VI_MOTION_Y_ADD (1<<28) +#define SOLO_VI_MOTION_CB_SET (1<<27) +#define SOLO_VI_MOTION_CB_ADD (1<<26) +#define SOLO_VI_MOTION_CR_SET (1<<25) +#define SOLO_VI_MOTION_CR_ADD (1<<24) +#define SOLO_VI_MOTION_Y_VALUE(v) ((v)<<16) +#define SOLO_VI_MOTION_CB_VALUE(v) ((v)<<8) +#define SOLO_VI_MOTION_CR_VALUE(v) ((v)<<0) + +#define SOLO_VO_FMT_ENC 0x0300 +#define SOLO_VO_SCAN_MODE_PROGRESSIVE (1<<31) +#define SOLO_VO_FMT_TYPE_PAL (1<<30) +#define SOLO_VO_FMT_TYPE_NTSC 0 +#define SOLO_VO_USER_SET (1<<29) + +#define SOLO_VO_FI_CHANGE (1<<20) +#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19) +#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18) +#define SOLO_VO_USER_COLOR_SET_NAV (1<<17) +#define SOLO_VO_USER_COLOR_SET_NAH (1<<16) +#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8) +#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4) +#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0) + +#define SOLO_VO_ACT_H 0x0304 +#define SOLO_VO_H_BLANK(n) ((n)<<22) +#define SOLO_VO_H_START(n) ((n)<<11) +#define SOLO_VO_H_STOP(n) ((n)<<0) + +#define SOLO_VO_ACT_V 0x0308 +#define SOLO_VO_V_BLANK(n) ((n)<<22) +#define SOLO_VO_V_START(n) ((n)<<11) +#define SOLO_VO_V_STOP(n) ((n)<<0) + +#define SOLO_VO_RANGE_HV 0x030C +#define SOLO_VO_SYNC_INVERT (1<<24) +#define SOLO_VO_HSYNC_INVERT (1<<23) +#define SOLO_VO_VSYNC_INVERT (1<<22) +#define SOLO_VO_H_LEN(n) ((n)<<11) +#define SOLO_VO_V_LEN(n) ((n)<<0) + +#define SOLO_VO_DISP_CTRL 0x0310 +#define SOLO_VO_DISP_ON (1<<31) +#define SOLO_VO_DISP_ERASE_COUNT(n) ((n&0xf)<<24) +#define SOLO_VO_DISP_DOUBLE_SCAN (1<<22) +#define SOLO_VO_DISP_SINGLE_PAGE (1<<21) +#define SOLO_VO_DISP_BASE(n) (((n)>>16) & 0xffff) + +#define SOLO_VO_DISP_ERASE 0x0314 +#define SOLO_VO_DISP_ERASE_ON (1<<0) + +#define SOLO_VO_ZOOM_CTRL 0x0318 +#define SOLO_VO_ZOOM_VER_ON (1<<24) +#define SOLO_VO_ZOOM_HOR_ON (1<<23) +#define SOLO_VO_ZOOM_V_COMP (1<<22) +#define SOLO_VO_ZOOM_SX(h) (((h)/2)<<11) +#define SOLO_VO_ZOOM_SY(v) (((v)/2)<<0) + +#define SOLO_VO_FREEZE_CTRL 0x031C +#define SOLO_VO_FREEZE_ON (1<<1) +#define SOLO_VO_FREEZE_INTERPOLATION (1<<0) + +#define SOLO_VO_BKG_COLOR 0x0320 +#define SOLO_BG_Y(y) ((y)<<16) +#define SOLO_BG_U(u) ((u)<<8) +#define SOLO_BG_V(v) ((v)<<0) + +#define SOLO_VO_DEINTERLACE 0x0324 +#define SOLO_VO_DEINTERLACE_THRESHOLD(n) ((n)<<8) +#define SOLO_VO_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) + +#define SOLO_VO_BORDER_LINE_COLOR 0x0330 +#define SOLO_VO_BORDER_FILL_COLOR 0x0334 +#define SOLO_VO_BORDER_LINE_MASK 0x0338 +#define SOLO_VO_BORDER_FILL_MASK 0x033c + +#define SOLO_VO_BORDER_X(n) (0x0340+((n)*4)) +#define SOLO_VO_BORDER_Y(n) (0x0354+((n)*4)) + +#define SOLO_VO_CELL_EXT_SET 0x0368 +#define SOLO_VO_CELL_EXT_START 0x036c +#define SOLO_VO_CELL_EXT_STOP 0x0370 + +#define SOLO_VO_CELL_EXT_SET2 0x0374 +#define SOLO_VO_CELL_EXT_START2 0x0378 +#define SOLO_VO_CELL_EXT_STOP2 0x037c + +#define SOLO_VO_RECTANGLE_CTRL(n) (0x0368+((n)*12)) +#define SOLO_VO_RECTANGLE_START(n) (0x036c+((n)*12)) +#define SOLO_VO_RECTANGLE_STOP(n) (0x0370+((n)*12)) + +#define SOLO_VO_CURSOR_POS (0x0380) +#define SOLO_VO_CURSOR_CLR (0x0384) +#define SOLO_VO_CURSOR_CLR2 (0x0388) +#define SOLO_VO_CURSOR_MASK(id) (0x0390+((id)*4)) + +#define SOLO_VO_EXPANSION(id) (0x0250+((id)*4)) + +#define SOLO_OSG_CONFIG 0x03E0 +#define SOLO_VO_OSG_ON (1<<31) +#define SOLO_VO_OSG_COLOR_MUTE (1<<28) +#define SOLO_VO_OSG_ALPHA_RATE(n) ((n)<<22) +#define SOLO_VO_OSG_ALPHA_BG_RATE(n) ((n)<<16) +#define SOLO_VO_OSG_BASE(offset) (((offset)>>16)&0xffff) + +#define SOLO_OSG_ERASE 0x03E4 +#define SOLO_OSG_ERASE_ON (0x80) +#define SOLO_OSG_ERASE_OFF (0x00) + +#define SOLO_VO_OSG_BLINK 0x03E8 +#define SOLO_VO_OSG_BLINK_ON (1<<1) +#define SOLO_VO_OSG_BLINK_INTREVAL18 (1<<0) + +#define SOLO_CAP_BASE 0x0400 +#define SOLO_CAP_MAX_PAGE(n) ((n)<<16) +#define SOLO_CAP_BASE_ADDR(n) ((n)<<0) +#define SOLO_CAP_BTW 0x0404 +#define SOLO_CAP_PROG_BANDWIDTH(n) ((n)<<8) +#define SOLO_CAP_MAX_BANDWIDTH(n) ((n)<<0) + +#define SOLO_DIM_SCALE1 0x0408 +#define SOLO_DIM_SCALE2 0x040C +#define SOLO_DIM_SCALE3 0x0410 +#define SOLO_DIM_SCALE4 0x0414 +#define SOLO_DIM_SCALE5 0x0418 +#define SOLO_DIM_V_MB_NUM_FRAME(n) ((n)<<16) +#define SOLO_DIM_V_MB_NUM_FIELD(n) ((n)<<8) +#define SOLO_DIM_H_MB_NUM(n) ((n)<<0) + +#define SOLO_DIM_PROG 0x041C +#define SOLO_CAP_STATUS 0x0420 + +#define SOLO_CAP_CH_SCALE(ch) (0x0440+((ch)*4)) +#define SOLO_CAP_CH_COMP_ENA_E(ch) (0x0480+((ch)*4)) +#define SOLO_CAP_CH_INTV(ch) (0x04C0+((ch)*4)) +#define SOLO_CAP_CH_INTV_E(ch) (0x0500+((ch)*4)) + + +#define SOLO_VE_CFG0 0x0610 +#define SOLO_VE_TWO_PAGE_MODE (1<<31) +#define SOLO_VE_INTR_CTRL(n) ((n)<<24) +#define SOLO_VE_BLOCK_SIZE(n) ((n)<<16) +#define SOLO_VE_BLOCK_BASE(n) ((n)<<0) + +#define SOLO_VE_CFG1 0x0614 +#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */ +#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */ +#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */ +#define SOLO_VE_INSERT_INDEX (1<<18) +#define SOLO_VE_MOTION_MODE(n) ((n)<<16) +#define SOLO_VE_MOTION_BASE(n) ((n)<<0) + +#define SOLO_VE_WMRK_POLY 0x061C +#define SOLO_VE_VMRK_INIT_KEY 0x0620 +#define SOLO_VE_WMRK_STRL 0x0624 +#define SOLO_VE_ENCRYP_POLY 0x0628 +#define SOLO_VE_ENCRYP_INIT 0x062C +#define SOLO_VE_ATTR 0x0630 +#define SOLO_VE_LITTLE_ENDIAN (1<<31) +#define SOLO_COMP_ATTR_RN (1<<30) +#define SOLO_COMP_ATTR_FCODE(n) ((n)<<27) +#define SOLO_COMP_TIME_INC(n) ((n)<<25) +#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21) +#define SOLO_DCT_INTERVAL(n) ((n)<<16) + +#define SOLO_VE_STATE(n) (0x0640+((n)*4)) + +#define SOLO_VE_JPEG_QP_TBL 0x0670 +#define SOLO_VE_JPEG_QP_CH_L 0x0674 +#define SOLO_VE_JPEG_QP_CH_H 0x0678 +#define SOLO_VE_JPEG_CFG 0x067C +#define SOLO_VE_JPEG_CTRL 0x0680 + +#define SOLO_VE_OSD_CH 0x0690 +#define SOLO_VE_OSD_BASE 0x0694 +#define SOLO_VE_OSD_CLR 0x0698 +#define SOLO_VE_OSD_OPT 0x069C + +#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4)) +#define SOLO6010_VE_CH_MOT(ch) (0x0740+((ch)*4)) /* 6010 only */ +#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4)) +#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4)) +#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4)) +#define SOLO_VE_CH_GOP_E(ch) (0x0840+((ch)*4)) +#define SOLO_VE_CH_REF_BASE(ch) (0x0880+((ch)*4)) +#define SOLO_VE_CH_REF_BASE_E(ch) (0x08C0+((ch)*4)) + +#define SOLO_VE_MPEG4_QUE(n) (0x0A00+((n)*8)) +#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8)) + +#define SOLO_VD_CFG0 0x0900 +#define SOLO6010_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) /* 6010 only */ +#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23) +#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22) +#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21) +#define SOLO_VD_CFG_BUSY_WIAT_MS (1<<20) +#define SOLO_VD_CFG_SINGLE_MODE (1<<18) +#define SOLO_VD_CFG_SCAL_MANUAL (1<<17) +#define SOLO_VD_CFG_USER_PAGE_CTRL (1<<16) +#define SOLO_VD_CFG_LITTLE_ENDIAN (1<<15) +#define SOLO_VD_CFG_START_FI (1<<14) +#define SOLO_VD_CFG_ERR_LOCK (1<<13) +#define SOLO_VD_CFG_ERR_INT_ENA (1<<12) +#define SOLO_VD_CFG_TIME_WIDTH(n) ((n)<<8) +#define SOLO_VD_CFG_DCT_INTERVAL(n) ((n)<<0) + +#define SOLO_VD_CFG1 0x0904 + +#define SOLO_VD_DEINTERLACE 0x0908 +#define SOLO_VD_DEINTERLACE_THRESHOLD(n) ((n)<<8) +#define SOLO_VD_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) + +#define SOLO_VD_CODE_ADR 0x090C + +#define SOLO_VD_CTRL 0x0910 +#define SOLO_VD_OPER_ON (1<<31) +#define SOLO_VD_MAX_ITEM(n) ((n)<<0) + +#define SOLO_VD_STATUS0 0x0920 +#define SOLO_VD_STATUS0_INTR_ACK (1<<22) +#define SOLO_VD_STATUS0_INTR_EMPTY (1<<21) +#define SOLO_VD_STATUS0_INTR_ERR (1<<20) + +#define SOLO_VD_STATUS1 0x0924 + +#define SOLO_VD_IDX0 0x0930 +#define SOLO_VD_IDX_INTERLACE (1<<30) +#define SOLO_VD_IDX_CHANNEL(n) ((n)<<24) +#define SOLO_VD_IDX_SIZE(n) ((n)<<0) + +#define SOLO_VD_IDX1 0x0934 +#define SOLO_VD_IDX_SRC_SCALE(n) ((n)<<28) +#define SOLO_VD_IDX_WINDOW(n) ((n)<<24) +#define SOLO_VD_IDX_DEINTERLACE (1<<16) +#define SOLO_VD_IDX_H_BLOCK(n) ((n)<<8) +#define SOLO_VD_IDX_V_BLOCK(n) ((n)<<0) + +#define SOLO_VD_IDX2 0x0938 +#define SOLO_VD_IDX_REF_BASE_SIDE (1<<31) +#define SOLO_VD_IDX_REF_BASE(n) (((n)>>16)&0xffff) + +#define SOLO_VD_IDX3 0x093C +#define SOLO_VD_IDX_DISP_SCALE(n) ((n)<<28) +#define SOLO_VD_IDX_INTERLACE_WR (1<<27) +#define SOLO_VD_IDX_INTERPOL (1<<26) +#define SOLO_VD_IDX_HOR2X (1<<25) +#define SOLO_VD_IDX_OFFSET_X(n) ((n)<<12) +#define SOLO_VD_IDX_OFFSET_Y(n) ((n)<<0) + +#define SOLO_VD_IDX4 0x0940 +#define SOLO_VD_IDX_DEC_WR_PAGE(n) ((n)<<8) +#define SOLO_VD_IDX_DISP_RD_PAGE(n) ((n)<<0) + +#define SOLO_VD_WR_PAGE(n) (0x03F0 + ((n) * 4)) + + +#define SOLO_GPIO_CONFIG_0 0x0B00 +#define SOLO_GPIO_CONFIG_1 0x0B04 +#define SOLO_GPIO_DATA_OUT 0x0B08 +#define SOLO_GPIO_DATA_IN 0x0B0C +#define SOLO_GPIO_INT_ACK_STA 0x0B10 +#define SOLO_GPIO_INT_ENA 0x0B14 +#define SOLO_GPIO_INT_CFG_0 0x0B18 +#define SOLO_GPIO_INT_CFG_1 0x0B1C + + +#define SOLO_IIC_CFG 0x0B20 +#define SOLO_IIC_ENABLE (1<<8) +#define SOLO_IIC_PRESCALE(n) ((n)<<0) + +#define SOLO_IIC_CTRL 0x0B24 +#define SOLO_IIC_AUTO_CLEAR (1<<20) +#define SOLO_IIC_STATE_RX_ACK (1<<19) +#define SOLO_IIC_STATE_BUSY (1<<18) +#define SOLO_IIC_STATE_SIG_ERR (1<<17) +#define SOLO_IIC_STATE_TRNS (1<<16) +#define SOLO_IIC_CH_SET(n) ((n)<<5) +#define SOLO_IIC_ACK_EN (1<<4) +#define SOLO_IIC_START (1<<3) +#define SOLO_IIC_STOP (1<<2) +#define SOLO_IIC_READ (1<<1) +#define SOLO_IIC_WRITE (1<<0) + +#define SOLO_IIC_TXD 0x0B28 +#define SOLO_IIC_RXD 0x0B2C + +/* + * UART REGISTER + */ +#define SOLO_UART_CONTROL(n) (0x0BA0 + ((n)*0x20)) +#define SOLO_UART_CLK_DIV(n) ((n)<<24) +#define SOLO_MODEM_CTRL_EN (1<<20) +#define SOLO_PARITY_ERROR_DROP (1<<18) +#define SOLO_IRQ_ERR_EN (1<<17) +#define SOLO_IRQ_RX_EN (1<<16) +#define SOLO_IRQ_TX_EN (1<<15) +#define SOLO_RX_EN (1<<14) +#define SOLO_TX_EN (1<<13) +#define SOLO_UART_HALF_DUPLEX (1<<12) +#define SOLO_UART_LOOPBACK (1<<11) + +#define SOLO_BAUDRATE_230400 ((0<<9)|(0<<6)) +#define SOLO_BAUDRATE_115200 ((0<<9)|(1<<6)) +#define SOLO_BAUDRATE_57600 ((0<<9)|(2<<6)) +#define SOLO_BAUDRATE_38400 ((0<<9)|(3<<6)) +#define SOLO_BAUDRATE_19200 ((0<<9)|(4<<6)) +#define SOLO_BAUDRATE_9600 ((0<<9)|(5<<6)) +#define SOLO_BAUDRATE_4800 ((0<<9)|(6<<6)) +#define SOLO_BAUDRATE_2400 ((1<<9)|(6<<6)) +#define SOLO_BAUDRATE_1200 ((2<<9)|(6<<6)) +#define SOLO_BAUDRATE_300 ((3<<9)|(6<<6)) + +#define SOLO_UART_DATA_BIT_8 (3<<4) +#define SOLO_UART_DATA_BIT_7 (2<<4) +#define SOLO_UART_DATA_BIT_6 (1<<4) +#define SOLO_UART_DATA_BIT_5 (0<<4) + +#define SOLO_UART_STOP_BIT_1 (0<<2) +#define SOLO_UART_STOP_BIT_2 (1<<2) + +#define SOLO_UART_PARITY_NONE (0<<0) +#define SOLO_UART_PARITY_EVEN (2<<0) +#define SOLO_UART_PARITY_ODD (3<<0) + +#define SOLO_UART_STATUS(n) (0x0BA4 + ((n)*0x20)) +#define SOLO_UART_CTS (1<<15) +#define SOLO_UART_RX_BUSY (1<<14) +#define SOLO_UART_OVERRUN (1<<13) +#define SOLO_UART_FRAME_ERR (1<<12) +#define SOLO_UART_PARITY_ERR (1<<11) +#define SOLO_UART_TX_BUSY (1<<5) + +#define SOLO_UART_RX_BUFF_CNT(stat) (((stat)>>6) & 0x1f) +#define SOLO_UART_RX_BUFF_SIZE 8 +#define SOLO_UART_TX_BUFF_CNT(stat) (((stat)>>0) & 0x1f) +#define SOLO_UART_TX_BUFF_SIZE 8 + +#define SOLO_UART_TX_DATA(n) (0x0BA8 + ((n)*0x20)) +#define SOLO_UART_TX_DATA_PUSH (1<<8) +#define SOLO_UART_RX_DATA(n) (0x0BAC + ((n)*0x20)) +#define SOLO_UART_RX_DATA_POP (1<<8) + +#define SOLO_TIMER_CLOCK_NUM 0x0be0 +#define SOLO_TIMER_WATCHDOG 0x0be4 +#define SOLO_TIMER_USEC 0x0be8 +#define SOLO_TIMER_SEC 0x0bec + +#define SOLO_AUDIO_CONTROL 0x0D00 +#define SOLO_AUDIO_ENABLE (1<<31) +#define SOLO_AUDIO_MASTER_MODE (1<<30) +#define SOLO_AUDIO_I2S_MODE (1<<29) +#define SOLO_AUDIO_I2S_LR_SWAP (1<<27) +#define SOLO_AUDIO_I2S_8BIT (1<<26) +#define SOLO_AUDIO_I2S_MULTI(n) ((n)<<24) +#define SOLO_AUDIO_MIX_9TO0 (1<<23) +#define SOLO_AUDIO_DEC_9TO0_VOL(n) ((n)<<20) +#define SOLO_AUDIO_MIX_19TO10 (1<<19) +#define SOLO_AUDIO_DEC_19TO10_VOL(n) ((n)<<16) +#define SOLO_AUDIO_MODE(n) ((n)<<0) +#define SOLO_AUDIO_SAMPLE 0x0D04 +#define SOLO_AUDIO_EE_MODE_ON (1<<30) +#define SOLO_AUDIO_EE_ENC_CH(ch) ((ch)<<25) +#define SOLO_AUDIO_BITRATE(n) ((n)<<16) +#define SOLO_AUDIO_CLK_DIV(n) ((n)<<0) +#define SOLO_AUDIO_FDMA_INTR 0x0D08 +#define SOLO_AUDIO_FDMA_INTERVAL(n) ((n)<<19) +#define SOLO_AUDIO_INTR_ORDER(n) ((n)<<16) +#define SOLO_AUDIO_FDMA_BASE(n) ((n)<<0) +#define SOLO_AUDIO_EVOL_0 0x0D0C +#define SOLO_AUDIO_EVOL_1 0x0D10 +#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10)) +#define SOLO_AUDIO_STA 0x0D14 + + +#define SOLO_WATCHDOG 0x0BE4 +#define WATCHDOG_STAT(status) (status<<8) +#define WATCHDOG_TIME(sec) (sec&0xff) + +#endif /* __SOLO6010_REGISTERS_H */ diff --git a/drivers/staging/solo6x10/solo6010-core.c b/drivers/staging/solo6x10/solo6010-core.c deleted file mode 100644 index c713a74..0000000 --- a/drivers/staging/solo6x10/solo6010-core.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include - -#include "solo6010.h" -#include "solo6010-tw28.h" - -MODULE_DESCRIPTION("Softlogic 6010 MP4 Encoder/Decoder V4L2/ALSA Driver"); -MODULE_AUTHOR("Ben Collins "); -MODULE_VERSION(SOLO6010_VERSION); -MODULE_LICENSE("GPL"); - -void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask) -{ - solo_dev->irq_mask |= mask; - solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); -} - -void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask) -{ - solo_dev->irq_mask &= ~mask; - solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); -} - -/* XXX We should check the return value of the sub-device ISR's */ -static irqreturn_t solo6010_isr(int irq, void *data) -{ - struct solo6010_dev *solo_dev = data; - u32 status; - int i; - - status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); - if (!status) - return IRQ_NONE; - - if (status & ~solo_dev->irq_mask) { - solo_reg_write(solo_dev, SOLO_IRQ_STAT, - status & ~solo_dev->irq_mask); - status &= solo_dev->irq_mask; - } - - if (status & SOLO_IRQ_PCI_ERR) { - u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR); - solo_p2m_error_isr(solo_dev, err); - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR); - } - - for (i = 0; i < SOLO_NR_P2M; i++) - if (status & SOLO_IRQ_P2M(i)) - solo_p2m_isr(solo_dev, i); - - if (status & SOLO_IRQ_IIC) - solo_i2c_isr(solo_dev); - - if (status & SOLO_IRQ_VIDEO_IN) - solo_video_in_isr(solo_dev); - - /* Call this first so enc gets detected flag set */ - if (status & SOLO_IRQ_MOTION) - solo_motion_isr(solo_dev); - - if (status & SOLO_IRQ_ENCODER) - solo_enc_v4l2_isr(solo_dev); - - if (status & SOLO_IRQ_G723) - solo_g723_isr(solo_dev); - - return IRQ_HANDLED; -} - -static void free_solo_dev(struct solo6010_dev *solo_dev) -{ - struct pci_dev *pdev; - - if (!solo_dev) - return; - - pdev = solo_dev->pdev; - - /* If we never initialized the PCI device, then nothing else - * below here needs cleanup */ - if (!pdev) { - kfree(solo_dev); - return; - } - - /* Bring down the sub-devices first */ - solo_g723_exit(solo_dev); - solo_enc_v4l2_exit(solo_dev); - solo_enc_exit(solo_dev); - solo_v4l2_exit(solo_dev); - solo_disp_exit(solo_dev); - solo_gpio_exit(solo_dev); - solo_p2m_exit(solo_dev); - solo_i2c_exit(solo_dev); - - /* Now cleanup the PCI device */ - if (solo_dev->reg_base) { - solo6010_irq_off(solo_dev, ~0); - pci_iounmap(pdev, solo_dev->reg_base); - free_irq(pdev->irq, solo_dev); - } - - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - - kfree(solo_dev); -} - -static int __devinit solo6010_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct solo6010_dev *solo_dev; - int ret; - int sdram; - u8 chip_id; - u32 reg; - - solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); - if (solo_dev == NULL) - return -ENOMEM; - - solo_dev->pdev = pdev; - spin_lock_init(&solo_dev->reg_io_lock); - pci_set_drvdata(pdev, solo_dev); - - ret = pci_enable_device(pdev); - if (ret) - goto fail_probe; - - pci_set_master(pdev); - - ret = pci_request_regions(pdev, SOLO6010_NAME); - if (ret) - goto fail_probe; - - solo_dev->reg_base = pci_ioremap_bar(pdev, 0); - if (solo_dev->reg_base == NULL) { - ret = -ENOMEM; - goto fail_probe; - } - - chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & - SOLO_CHIP_ID_MASK; - switch (chip_id) { - case 7: - solo_dev->nr_chans = 16; - solo_dev->nr_ext = 5; - break; - case 6: - solo_dev->nr_chans = 8; - solo_dev->nr_ext = 2; - break; - default: - dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, " - "defaulting to 4 channels\n", - chip_id); - case 5: - solo_dev->nr_chans = 4; - solo_dev->nr_ext = 1; - } - - solo_dev->flags = id->driver_data; - - /* Disable all interrupts to start */ - solo6010_irq_off(solo_dev, ~0); - - reg = SOLO_SYS_CFG_SDRAM64BIT; - /* Initial global settings */ - if (!(solo_dev->flags & FLAGS_6110)) - reg |= SOLO6010_SYS_CFG_INPUTDIV(25) | - SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) | - SOLO6010_SYS_CFG_OUTDIV(3); - solo_reg_write(solo_dev, SOLO_SYS_CFG, reg); - - if (solo_dev->flags & FLAGS_6110) { - u32 sys_clock_MHz = SOLO_CLOCK_MHZ; - u32 pll_DIVQ; - u32 pll_DIVF; - - if (sys_clock_MHz < 125) { - pll_DIVQ = 3; - pll_DIVF = (sys_clock_MHz * 4) / 3; - } else { - pll_DIVQ = 2; - pll_DIVF = (sys_clock_MHz * 2) / 3; - } - - solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG, - SOLO6110_PLL_RANGE_5_10MHZ | - SOLO6110_PLL_DIVR(9) | - SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) | - SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN); - mdelay(1); // PLL Locking time (1ms) - - solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */ - } else - solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */ - - solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1); - - /* PLL locking time of 1ms */ - mdelay(1); - - ret = request_irq(pdev->irq, solo6010_isr, IRQF_SHARED, SOLO6010_NAME, - solo_dev); - if (ret) - goto fail_probe; - - /* Handle this from the start */ - solo6010_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); - - ret = solo_i2c_init(solo_dev); - if (ret) - goto fail_probe; - - /* Setup the DMA engine */ - sdram = (solo_dev->nr_chans >= 8) ? 2 : 1; - solo_reg_write(solo_dev, SOLO_DMA_CTRL, - SOLO_DMA_CTRL_REFRESH_CYCLE(1) | - SOLO_DMA_CTRL_SDRAM_SIZE(sdram) | - SOLO_DMA_CTRL_SDRAM_CLK_INVERT | - SOLO_DMA_CTRL_READ_CLK_SELECT | - SOLO_DMA_CTRL_LATENCY(1)); - - ret = solo_p2m_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_disp_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_gpio_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_tw28_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_v4l2_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_enc_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_enc_v4l2_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_g723_init(solo_dev); - if (ret) - goto fail_probe; - - return 0; - -fail_probe: - free_solo_dev(solo_dev); - return ret; -} - -static void __devexit solo6010_pci_remove(struct pci_dev *pdev) -{ - struct solo6010_dev *solo_dev = pci_get_drvdata(pdev); - - free_solo_dev(solo_dev); -} - -static struct pci_device_id solo6010_id_table[] = { - /* 6010 based cards */ - {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)}, - {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), - .driver_data = FLAGS_6110}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)}, - /* 6110 based cards */ - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)}, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, solo6010_id_table); - -static struct pci_driver solo6010_pci_driver = { - .name = SOLO6010_NAME, - .id_table = solo6010_id_table, - .probe = solo6010_pci_probe, - .remove = solo6010_pci_remove, -}; - -static int __init solo6010_module_init(void) -{ - return pci_register_driver(&solo6010_pci_driver); -} - -static void __exit solo6010_module_exit(void) -{ - pci_unregister_driver(&solo6010_pci_driver); -} - -module_init(solo6010_module_init); -module_exit(solo6010_module_exit); diff --git a/drivers/staging/solo6x10/solo6010-disp.c b/drivers/staging/solo6x10/solo6010-disp.c deleted file mode 100644 index 99d1461..0000000 --- a/drivers/staging/solo6x10/solo6010-disp.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include "solo6010.h" - -#define SOLO_VCLK_DELAY 3 -#define SOLO_PROGRESSIVE_VSIZE 1024 - -#define SOLO_MOT_THRESH_W 64 -#define SOLO_MOT_THRESH_H 64 -#define SOLO_MOT_THRESH_SIZE 8192 -#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) -#define SOLO_MOT_FLAG_SIZE 512 -#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32) - -static unsigned video_type; -module_param(video_type, uint, 0644); -MODULE_PARM_DESC(video_type, "video_type (0 = NTSC/Default, 1 = PAL)"); - -static void solo_vin_config(struct solo6010_dev *solo_dev) -{ - solo_dev->vin_hstart = 8; - solo_dev->vin_vstart = 2; - - solo_reg_write(solo_dev, SOLO_SYS_VCLK, - SOLO_VCLK_SELECT(2) | - SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, - SOLO_VI_H_START(solo_dev->vin_hstart) | - SOLO_VI_V_START(solo_dev->vin_vstart) | - SOLO_VI_V_STOP(solo_dev->vin_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, - SOLO_VI_H_START(solo_dev->vout_hstart) | - SOLO_VI_V_START(solo_dev->vout_vstart) | - SOLO_VI_V_STOP(solo_dev->vout_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_P, - SOLO_VI_H_START(0) | - SOLO_VI_V_START(1) | - SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); - - solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, - SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); - - solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); - solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, - SOLO_VI_PB_USER_MODE); - solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, - SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, - SOLO_VI_PB_VSTART(4) | - SOLO_VI_PB_VSTOP(4 + 240)); - } else { - solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, - SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); - solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, - SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, - SOLO_VI_PB_VSTART(4) | - SOLO_VI_PB_VSTOP(4 + 288)); - } - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | - SOLO_VI_PB_HSTOP(16 + 720)); -} - -static void solo_disp_config(struct solo6010_dev *solo_dev) -{ - solo_dev->vout_hstart = 6; - solo_dev->vout_vstart = 8; - - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, - (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, - (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); - solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, - (16 << 24) | (128 << 16) | (16 << 8) | 128); - - solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, - solo_dev->video_type | - SOLO_VO_USER_COLOR_SET_NAV | - SOLO_VO_NA_COLOR_Y(0) | - SOLO_VO_NA_COLOR_CB(0) | - SOLO_VO_NA_COLOR_CR(0)); - - solo_reg_write(solo_dev, SOLO_VO_ACT_H, - SOLO_VO_H_START(solo_dev->vout_hstart) | - SOLO_VO_H_STOP(solo_dev->vout_hstart + - solo_dev->video_hsize)); - - solo_reg_write(solo_dev, SOLO_VO_ACT_V, - SOLO_VO_V_START(solo_dev->vout_vstart) | - SOLO_VO_V_STOP(solo_dev->vout_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, - SOLO_VO_H_LEN(solo_dev->video_hsize) | - SOLO_VO_V_LEN(solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 5); - - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | - SOLO_VO_DISP_ERASE_COUNT(8) | - SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); - - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - - /* Enable channels we support */ - solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1); - - /* Disable the watchdog */ - solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); -} - -static int solo_dma_vin_region(struct solo6010_dev *solo_dev, u32 off, - u16 val, int reg_size) -{ - u16 buf[64]; - int i; - int ret = 0; - - for (i = 0; i < sizeof(buf) >> 1; i++) - buf[i] = val; - - for (i = 0; i < reg_size; i += sizeof(buf)) - ret |= solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_VIN, 1, buf, - SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, - sizeof(buf)); - - return ret; -} - -void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val) -{ - if (ch > solo_dev->nr_chans) - return; - - solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - (ch * SOLO_MOT_THRESH_SIZE * 2), - val, SOLO_MOT_THRESH_REAL); -} - -/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k - * threshold and working table for each channel. Atleast that's what the - * spec says. However, this code (take from rdk) has some mystery 8k - * block right after the flag area, before the first thresh table. */ -static void solo_motion_config(struct solo6010_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - /* Clear motion flag area */ - solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, - SOLO_MOT_FLAG_SIZE); - - /* Clear working cache table */ - solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - SOLO_MOT_THRESH_SIZE + - (i * SOLO_MOT_THRESH_SIZE * 2), - 0x0000, SOLO_MOT_THRESH_REAL); - - /* Set default threshold table */ - solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); - } - - /* Default motion settings */ - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, - SOLO_VI_MOTION_FRAME_COUNT(3) | - SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) - | /* SOLO_VI_MOTION_INTR_START_STOP | */ - SOLO_VI_MOTION_SAMPLE_COUNT(10)); - - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); -} - -int solo_disp_init(struct solo6010_dev *solo_dev) -{ - int i; - - solo_dev->video_hsize = 704; - if (video_type == 0) { - solo_dev->video_type = SOLO_VO_FMT_TYPE_NTSC; - solo_dev->video_vsize = 240; - solo_dev->fps = 30; - } else { - solo_dev->video_type = SOLO_VO_FMT_TYPE_PAL; - solo_dev->video_vsize = 288; - solo_dev->fps = 25; - } - - solo_vin_config(solo_dev); - solo_motion_config(solo_dev); - solo_disp_config(solo_dev); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); - - return 0; -} - -void solo_disp_exit(struct solo6010_dev *solo_dev) -{ - int i; - - solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION); - - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); - solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); - } - - /* Set default border */ - for (i = 0; i < 5; i++) - solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); - - for (i = 0; i < 5; i++) - solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); - - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); - - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); - - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); -} diff --git a/drivers/staging/solo6x10/solo6010-enc.c b/drivers/staging/solo6x10/solo6010-enc.c deleted file mode 100644 index 7a3c4d5..0000000 --- a/drivers/staging/solo6x10/solo6010-enc.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include - -#include "solo6010.h" -#include "solo6010-osd-font.h" - -#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */ -#define OSG_BUFFER_SIZE 1024 - -#define VI_PROG_HSIZE (1280 - 16) -#define VI_PROG_VSIZE (1024 - 16) - -static void solo_capture_config(struct solo6010_dev *solo_dev) -{ - int i, j; - unsigned long height; - unsigned long width; - unsigned char *buf; - - solo_reg_write(solo_dev, SOLO_CAP_BASE, - SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE * - solo_dev->nr_chans) | - SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); - solo_reg_write(solo_dev, SOLO_CAP_BTW, - (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | - SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH)); - - /* Set scale 1, 9 dimension */ - width = solo_dev->video_hsize; - height = solo_dev->video_vsize; - solo_reg_write(solo_dev, SOLO_DIM_SCALE1, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 2, 10 dimension */ - width = solo_dev->video_hsize / 2; - height = solo_dev->video_vsize; - solo_reg_write(solo_dev, SOLO_DIM_SCALE2, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 3, 11 dimension */ - width = solo_dev->video_hsize / 2; - height = solo_dev->video_vsize / 2; - solo_reg_write(solo_dev, SOLO_DIM_SCALE3, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 4, 12 dimension */ - width = solo_dev->video_hsize / 3; - height = solo_dev->video_vsize / 3; - solo_reg_write(solo_dev, SOLO_DIM_SCALE4, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 5, 13 dimension */ - width = solo_dev->video_hsize / 4; - height = solo_dev->video_vsize / 2; - solo_reg_write(solo_dev, SOLO_DIM_SCALE5, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Progressive */ - width = VI_PROG_HSIZE; - height = VI_PROG_VSIZE; - solo_reg_write(solo_dev, SOLO_DIM_PROG, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 16) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Clear OSD */ - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, 0); - solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); - solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, - 0xF0 << 16 | 0x80 << 8 | 0x80); - solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, 0); - - /* Clear OSG buffer */ - buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL); - if (!buf) - return; - - for (i = 0; i < solo_dev->nr_chans; i++) { - for (j = 0; j < SOLO_EOSD_EXT_SIZE; j += OSG_BUFFER_SIZE) { - solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf, - SOLO_EOSD_EXT_ADDR + - (i * SOLO_EOSD_EXT_SIZE) + j, - OSG_BUFFER_SIZE); - } - } - kfree(buf); -} - -int solo_osd_print(struct solo_enc_dev *solo_enc) -{ - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - char *str = solo_enc->osd_text; - u8 *buf; - u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); - int len = strlen(str); - int i, j; - int x = 1, y = 1; - - if (len == 0) { - reg &= ~(1 << solo_enc->ch); - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); - return 0; - } - - buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (i = 0; i < len; i++) { - for (j = 0; j < 16; j++) { - buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] = - (solo_osd_font[(str[i] * 4) + (j / 4)] - >> ((3 - (j % 4)) * 8)) & 0xff; - } - } - - solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR + - (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE); - reg |= (1 << solo_enc->ch); - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); - - kfree(buf); - - return 0; -} - -static void solo_jpeg_config(struct solo6010_dev *solo_dev) -{ - u32 reg; - if (solo_dev->flags & FLAGS_6110) - reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0); - else - reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0); - solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, - (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | - ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); - solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); - /* que limit, samp limit, pos limit */ - solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60); -} - -static void solo_mp4e_config(struct solo6010_dev *solo_dev) -{ - int i; - u32 reg; - - /* We can only use VE_INTR_CTRL(0) if we want to support mjpeg */ - solo_reg_write(solo_dev, SOLO_VE_CFG0, - SOLO_VE_INTR_CTRL(0) | - SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | - SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); - - solo_reg_write(solo_dev, SOLO_VE_CFG1, - SOLO_VE_INSERT_INDEX | SOLO_VE_MOTION_MODE(0)); - - solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); - solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); - solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); - solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); - solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); - - reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) | - SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15); - if (solo_dev->flags & FLAGS_6110) - reg |= SOLO_DCT_INTERVAL(10); - else - reg |= SOLO_DCT_INTERVAL(36 / 4); - solo_reg_write(solo_dev, SOLO_VE_ATTR, reg); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), - (SOLO_EREF_EXT_ADDR(solo_dev) + - (i * SOLO_EREF_EXT_SIZE)) >> 16); - - if (solo_dev->flags & FLAGS_6110) - solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */ -} - -int solo_enc_init(struct solo6010_dev *solo_dev) -{ - int i; - - solo_capture_config(solo_dev); - solo_mp4e_config(solo_dev); - solo_jpeg_config(solo_dev); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); - } - - solo6010_irq_on(solo_dev, SOLO_IRQ_ENCODER); - - return 0; -} - -void solo_enc_exit(struct solo6010_dev *solo_dev) -{ - int i; - - solo6010_irq_off(solo_dev, SOLO_IRQ_ENCODER); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); - } -} diff --git a/drivers/staging/solo6x10/solo6010-g723.c b/drivers/staging/solo6x10/solo6010-g723.c deleted file mode 100644 index 254b46a..0000000 --- a/drivers/staging/solo6x10/solo6010-g723.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "solo6010.h" -#include "solo6010-tw28.h" - -#define G723_INTR_ORDER 0 -#define G723_FDMA_PAGES 32 -#define G723_PERIOD_BYTES 48 -#define G723_PERIOD_BLOCK 1024 -#define G723_FRAMES_PER_PAGE 48 - -/* Sets up channels 16-19 for decoding and 0-15 for encoding */ -#define OUTMODE_MASK 0x300 - -#define SAMPLERATE 8000 -#define BITRATE 25 - -/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page - * is broken down to 20 * 48 byte regions (one for each channel possible) - * with the rest of the page being dummy data. */ -#define MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) -#define IRQ_PAGES 4 /* 0 - 4 */ -#define PERIODS_MIN (1 << IRQ_PAGES) -#define PERIODS_MAX G723_FDMA_PAGES - -struct solo_snd_pcm { - int on; - spinlock_t lock; - struct solo6010_dev *solo_dev; - unsigned char g723_buf[G723_PERIOD_BYTES]; -}; - -static void solo_g723_config(struct solo6010_dev *solo_dev) -{ - int clk_div; - - clk_div = SOLO_CLOCK_MHZ / (SAMPLERATE * (BITRATE * 2) * 2); - - solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, - SOLO_AUDIO_BITRATE(BITRATE) | - SOLO_AUDIO_CLK_DIV(clk_div)); - - solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, - SOLO_AUDIO_FDMA_INTERVAL(IRQ_PAGES) | - SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) | - SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); - - solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, - SOLO_AUDIO_ENABLE | SOLO_AUDIO_I2S_MODE | - SOLO_AUDIO_I2S_MULTI(3) | SOLO_AUDIO_MODE(OUTMODE_MASK)); -} - -void solo_g723_isr(struct solo6010_dev *solo_dev) -{ - struct snd_pcm_str *pstr = - &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; - struct snd_pcm_substream *ss; - struct solo_snd_pcm *solo_pcm; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723); - - for (ss = pstr->substream; ss != NULL; ss = ss->next) { - if (snd_pcm_substream_chip(ss) == NULL) - continue; - - /* This means open() hasn't been called on this one */ - if (snd_pcm_substream_chip(ss) == solo_dev) - continue; - - /* Haven't triggered a start yet */ - solo_pcm = snd_pcm_substream_chip(ss); - if (!solo_pcm->on) - continue; - - snd_pcm_period_elapsed(ss); - } -} - -static int snd_solo_hw_params(struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); -} - -static int snd_solo_hw_free(struct snd_pcm_substream *ss) -{ - return snd_pcm_lib_free_pages(ss); -} - -static struct snd_pcm_hardware snd_solo_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_U8, - .rates = SNDRV_PCM_RATE_8000, - .rate_min = 8000, - .rate_max = 8000, - .channels_min = 1, - .channels_max = 1, - .buffer_bytes_max = MAX_BUFFER, - .period_bytes_min = G723_PERIOD_BYTES, - .period_bytes_max = G723_PERIOD_BYTES, - .periods_min = PERIODS_MIN, - .periods_max = PERIODS_MAX, -}; - -static int snd_solo_pcm_open(struct snd_pcm_substream *ss) -{ - struct solo6010_dev *solo_dev = snd_pcm_substream_chip(ss); - struct solo_snd_pcm *solo_pcm; - - solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); - if (solo_pcm == NULL) - return -ENOMEM; - - spin_lock_init(&solo_pcm->lock); - solo_pcm->solo_dev = solo_dev; - ss->runtime->hw = snd_solo_pcm_hw; - - snd_pcm_substream_chip(ss) = solo_pcm; - - return 0; -} - -static int snd_solo_pcm_close(struct snd_pcm_substream *ss) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - - snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; - kfree(solo_pcm); - - return 0; -} - -static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo6010_dev *solo_dev = solo_pcm->solo_dev; - int ret = 0; - - spin_lock(&solo_pcm->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (solo_pcm->on == 0) { - /* If this is the first user, switch on interrupts */ - if (atomic_inc_return(&solo_dev->snd_users) == 1) - solo6010_irq_on(solo_dev, SOLO_IRQ_G723); - solo_pcm->on = 1; - } - break; - case SNDRV_PCM_TRIGGER_STOP: - if (solo_pcm->on) { - /* If this was our last user, switch them off */ - if (atomic_dec_return(&solo_dev->snd_users) == 0) - solo6010_irq_off(solo_dev, SOLO_IRQ_G723); - solo_pcm->on = 0; - } - break; - default: - ret = -EINVAL; - } - - spin_unlock(&solo_pcm->lock); - - return ret; -} - -static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) -{ - return 0; -} - -static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo6010_dev *solo_dev = solo_pcm->solo_dev; - snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; - - return idx * G723_FRAMES_PER_PAGE; -} - -static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, - snd_pcm_uframes_t pos, void __user *dst, - snd_pcm_uframes_t count) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo6010_dev *solo_dev = solo_pcm->solo_dev; - int err, i; - - for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { - int page = (pos / G723_FRAMES_PER_PAGE) + i; - - err = solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_G723E, 0, - solo_pcm->g723_buf, - SOLO_G723_EXT_ADDR(solo_dev) + - (page * G723_PERIOD_BLOCK) + - (ss->number * G723_PERIOD_BYTES), - G723_PERIOD_BYTES); - if (err) - return err; - - err = copy_to_user(dst + (i * G723_PERIOD_BYTES), - solo_pcm->g723_buf, G723_PERIOD_BYTES); - - if (err) - return -EFAULT; - } - - return 0; -} - -static struct snd_pcm_ops snd_solo_pcm_ops = { - .open = snd_solo_pcm_open, - .close = snd_solo_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_solo_hw_params, - .hw_free = snd_solo_hw_free, - .prepare = snd_solo_pcm_prepare, - .trigger = snd_solo_pcm_trigger, - .pointer = snd_solo_pcm_pointer, - .copy = snd_solo_pcm_copy, -}; - -static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 1; - info->value.integer.min = 0; - info->value.integer.max = 15; - info->value.integer.step = 1; - - return 0; -} - -static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol); - u8 ch = value->id.numid - 1; - - value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); - - return 0; -} - -static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct solo6010_dev *solo_dev = snd_kcontrol_chip(kcontrol); - u8 ch = value->id.numid - 1; - u8 old_val; - - old_val = tw28_get_audio_gain(solo_dev, ch); - if (old_val == value->value.integer.value[0]) - return 0; - - tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); - - return 1; -} - -static struct snd_kcontrol_new snd_solo_capture_volume = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .info = snd_solo_capture_volume_info, - .get = snd_solo_capture_volume_get, - .put = snd_solo_capture_volume_put, -}; - -static int solo_snd_pcm_init(struct solo6010_dev *solo_dev) -{ - struct snd_card *card = solo_dev->snd_card; - struct snd_pcm *pcm; - struct snd_pcm_substream *ss; - int ret; - int i; - - ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, - &pcm); - if (ret < 0) - return ret; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_solo_pcm_ops); - - snd_pcm_chip(pcm) = solo_dev; - pcm->info_flags = 0; - strcpy(pcm->name, card->shortname); - - for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - ss; ss = ss->next, i++) - sprintf(ss->name, "Camera #%d Audio", i); - - ret = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - MAX_BUFFER, MAX_BUFFER); - if (ret < 0) - return ret; - - solo_dev->snd_pcm = pcm; - - return 0; -} - -int solo_g723_init(struct solo6010_dev *solo_dev) -{ - static struct snd_device_ops ops = { NULL }; - struct snd_card *card; - struct snd_kcontrol_new kctl; - char name[32]; - int ret; - - atomic_set(&solo_dev->snd_users, 0); - - /* Allows for easier mapping between video and audio */ - sprintf(name, "Softlogic%d", solo_dev->vfd->num); - - ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, - &solo_dev->snd_card); - if (ret < 0) - return ret; - - card = solo_dev->snd_card; - - strcpy(card->driver, SOLO6010_NAME); - strcpy(card->shortname, "SOLO-6010 Audio"); - sprintf(card->longname, "%s on %s IRQ %d", card->shortname, - pci_name(solo_dev->pdev), solo_dev->pdev->irq); - snd_card_set_dev(card, &solo_dev->pdev->dev); - - ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); - if (ret < 0) - goto snd_error; - - /* Mixer controls */ - strcpy(card->mixername, "SOLO-6010"); - kctl = snd_solo_capture_volume; - kctl.count = solo_dev->nr_chans; - ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); - if (ret < 0) - return ret; - - ret = solo_snd_pcm_init(solo_dev); - if (ret < 0) - goto snd_error; - - ret = snd_card_register(card); - if (ret < 0) - goto snd_error; - - solo_g723_config(solo_dev); - - dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); - - return 0; - -snd_error: - snd_card_free(card); - return ret; -} - -void solo_g723_exit(struct solo6010_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); - solo6010_irq_off(solo_dev, SOLO_IRQ_G723); - - snd_card_free(solo_dev->snd_card); -} diff --git a/drivers/staging/solo6x10/solo6010-gpio.c b/drivers/staging/solo6x10/solo6010-gpio.c deleted file mode 100644 index 8869b88..0000000 --- a/drivers/staging/solo6x10/solo6010-gpio.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "solo6010.h" - -static void solo_gpio_mode(struct solo6010_dev *solo_dev, - unsigned int port_mask, unsigned int mode) -{ - int port; - unsigned int ret; - - ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0); - - /* To set gpio */ - for (port = 0; port < 16; port++) { - if (!((1 << port) & port_mask)) - continue; - - ret &= (~(3 << (port << 1))); - ret |= ((mode & 3) << (port << 1)); - } - - solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret); - - /* To set extended gpio - sensor */ - ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1); - - for (port = 0; port < 16; port++) { - if (!((1 << (port + 16)) & port_mask)) - continue; - - if (!mode) - ret &= ~(1 << port); - else - ret |= 1 << port; - } - - solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret); -} - -static void solo_gpio_set(struct solo6010_dev *solo_dev, unsigned int value) -{ - solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, - solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value); -} - -static void solo_gpio_clear(struct solo6010_dev *solo_dev, unsigned int value) -{ - solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, - solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value); -} - -static void solo_gpio_config(struct solo6010_dev *solo_dev) -{ - /* Video reset */ - solo_gpio_mode(solo_dev, 0x30, 1); - solo_gpio_clear(solo_dev, 0x30); - udelay(100); - solo_gpio_set(solo_dev, 0x30); - udelay(100); - - /* Warning: Don't touch the next line unless you're sure of what - * you're doing: first four gpio [0-3] are used for video. */ - solo_gpio_mode(solo_dev, 0x0f, 2); - - /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */ - solo_gpio_mode(solo_dev, 0xff00, 1); - - /* Initially set relay status to 0 */ - solo_gpio_clear(solo_dev, 0xff00); -} - -int solo_gpio_init(struct solo6010_dev *solo_dev) -{ - solo_gpio_config(solo_dev); - return 0; -} - -void solo_gpio_exit(struct solo6010_dev *solo_dev) -{ - solo_gpio_clear(solo_dev, 0x30); - solo_gpio_config(solo_dev); -} diff --git a/drivers/staging/solo6x10/solo6010-i2c.c b/drivers/staging/solo6x10/solo6010-i2c.c deleted file mode 100644 index 60b69cd..0000000 --- a/drivers/staging/solo6x10/solo6010-i2c.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* XXX: The SOLO6010 i2c does not have separate interrupts for each i2c - * channel. The bus can only handle one i2c event at a time. The below handles - * this all wrong. We should be using the status registers to see if the bus - * is in use, and have a global lock to check the status register. Also, - * the bulk of the work should be handled out-of-interrupt. The ugly loops - * that occur during interrupt scare me. The ISR should merely signal - * thread context, ACK the interrupt, and move on. -- BenC */ - -#include - -#include "solo6010.h" - -u8 solo_i2c_readbyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off) -{ - struct i2c_msg msgs[2]; - u8 data; - - msgs[0].flags = 0; - msgs[0].addr = addr; - msgs[0].len = 1; - msgs[0].buf = &off; - - msgs[1].flags = I2C_M_RD; - msgs[1].addr = addr; - msgs[1].len = 1; - msgs[1].buf = &data; - - i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); - - return data; -} - -void solo_i2c_writebyte(struct solo6010_dev *solo_dev, int id, u8 addr, - u8 off, u8 data) -{ - struct i2c_msg msgs; - u8 buf[2]; - - buf[0] = off; - buf[1] = data; - msgs.flags = 0; - msgs.addr = addr; - msgs.len = 2; - msgs.buf = buf; - - i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); -} - -static void solo_i2c_flush(struct solo6010_dev *solo_dev, int wr) -{ - u32 ctrl; - - ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); - - if (solo_dev->i2c_state == IIC_STATE_START) - ctrl |= SOLO_IIC_START; - - if (wr) { - ctrl |= SOLO_IIC_WRITE; - } else { - ctrl |= SOLO_IIC_READ; - if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) - ctrl |= SOLO_IIC_ACK_EN; - } - - if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) - ctrl |= SOLO_IIC_STOP; - - solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); -} - -static void solo_i2c_start(struct solo6010_dev *solo_dev) -{ - u32 addr = solo_dev->i2c_msg->addr << 1; - - if (solo_dev->i2c_msg->flags & I2C_M_RD) - addr |= 1; - - solo_dev->i2c_state = IIC_STATE_START; - solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); - solo_i2c_flush(solo_dev, 1); -} - -static void solo_i2c_stop(struct solo6010_dev *solo_dev) -{ - solo6010_irq_off(solo_dev, SOLO_IRQ_IIC); - solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); - solo_dev->i2c_state = IIC_STATE_STOP; - wake_up(&solo_dev->i2c_wait); -} - -static int solo_i2c_handle_read(struct solo6010_dev *solo_dev) -{ -prepare_read: - if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { - solo_i2c_flush(solo_dev, 0); - return 0; - } - - solo_dev->i2c_msg_ptr = 0; - solo_dev->i2c_msg++; - solo_dev->i2c_msg_num--; - - if (solo_dev->i2c_msg_num == 0) { - solo_i2c_stop(solo_dev); - return 0; - } - - if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { - solo_i2c_start(solo_dev); - } else { - if (solo_dev->i2c_msg->flags & I2C_M_RD) - goto prepare_read; - else - solo_i2c_stop(solo_dev); - } - - return 0; -} - -static int solo_i2c_handle_write(struct solo6010_dev *solo_dev) -{ -retry_write: - if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { - solo_reg_write(solo_dev, SOLO_IIC_TXD, - solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); - solo_dev->i2c_msg_ptr++; - solo_i2c_flush(solo_dev, 1); - return 0; - } - - solo_dev->i2c_msg_ptr = 0; - solo_dev->i2c_msg++; - solo_dev->i2c_msg_num--; - - if (solo_dev->i2c_msg_num == 0) { - solo_i2c_stop(solo_dev); - return 0; - } - - if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { - solo_i2c_start(solo_dev); - } else { - if (solo_dev->i2c_msg->flags & I2C_M_RD) - solo_i2c_stop(solo_dev); - else - goto retry_write; - } - - return 0; -} - -int solo_i2c_isr(struct solo6010_dev *solo_dev) -{ - u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); - int ret = -EINVAL; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC); - - if (status & (SOLO_IIC_STATE_TRNS & SOLO_IIC_STATE_SIG_ERR) || - solo_dev->i2c_id < 0) { - solo_i2c_stop(solo_dev); - return -ENXIO; - } - - switch (solo_dev->i2c_state) { - case IIC_STATE_START: - if (solo_dev->i2c_msg->flags & I2C_M_RD) { - solo_dev->i2c_state = IIC_STATE_READ; - ret = solo_i2c_handle_read(solo_dev); - break; - } - - solo_dev->i2c_state = IIC_STATE_WRITE; - case IIC_STATE_WRITE: - ret = solo_i2c_handle_write(solo_dev); - break; - - case IIC_STATE_READ: - solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = - solo_reg_read(solo_dev, SOLO_IIC_RXD); - solo_dev->i2c_msg_ptr++; - - ret = solo_i2c_handle_read(solo_dev); - break; - - default: - solo_i2c_stop(solo_dev); - } - - return ret; -} - -static int solo_i2c_master_xfer(struct i2c_adapter *adap, - struct i2c_msg msgs[], int num) -{ - struct solo6010_dev *solo_dev = adap->algo_data; - unsigned long timeout; - int ret; - int i; - DEFINE_WAIT(wait); - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (&solo_dev->i2c_adap[i] == adap) - break; - } - - if (i == SOLO_I2C_ADAPTERS) - return num; /* XXX Right return value for failure? */ - - mutex_lock(&solo_dev->i2c_mutex); - solo_dev->i2c_id = i; - solo_dev->i2c_msg = msgs; - solo_dev->i2c_msg_num = num; - solo_dev->i2c_msg_ptr = 0; - - solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); - solo6010_irq_on(solo_dev, SOLO_IRQ_IIC); - solo_i2c_start(solo_dev); - - timeout = HZ / 2; - - for (;;) { - prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE); - - if (solo_dev->i2c_state == IIC_STATE_STOP) - break; - - timeout = schedule_timeout(timeout); - if (!timeout) - break; - - if (signal_pending(current)) - break; - } - - finish_wait(&solo_dev->i2c_wait, &wait); - ret = num - solo_dev->i2c_msg_num; - solo_dev->i2c_state = IIC_STATE_IDLE; - solo_dev->i2c_id = -1; - - mutex_unlock(&solo_dev->i2c_mutex); - - return ret; -} - -static u32 solo_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C; -} - -static struct i2c_algorithm solo_i2c_algo = { - .master_xfer = solo_i2c_master_xfer, - .functionality = solo_i2c_functionality, -}; - -int solo_i2c_init(struct solo6010_dev *solo_dev) -{ - int i; - int ret; - - solo_reg_write(solo_dev, SOLO_IIC_CFG, - SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); - - solo_dev->i2c_id = -1; - solo_dev->i2c_state = IIC_STATE_IDLE; - init_waitqueue_head(&solo_dev->i2c_wait); - mutex_init(&solo_dev->i2c_mutex); - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; - - snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", - SOLO6010_NAME, i); - adap->algo = &solo_i2c_algo; - adap->algo_data = solo_dev; - adap->retries = 1; - adap->dev.parent = &solo_dev->pdev->dev; - - ret = i2c_add_adapter(adap); - if (ret) { - adap->algo_data = NULL; - break; - } - } - - if (ret) { - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (!solo_dev->i2c_adap[i].algo_data) - break; - i2c_del_adapter(&solo_dev->i2c_adap[i]); - solo_dev->i2c_adap[i].algo_data = NULL; - } - return ret; - } - - dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n", - SOLO_I2C_ADAPTERS); - - return 0; -} - -void solo_i2c_exit(struct solo6010_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (!solo_dev->i2c_adap[i].algo_data) - continue; - i2c_del_adapter(&solo_dev->i2c_adap[i]); - solo_dev->i2c_adap[i].algo_data = NULL; - } -} diff --git a/drivers/staging/solo6x10/solo6010-jpeg.h b/drivers/staging/solo6x10/solo6010-jpeg.h deleted file mode 100644 index fb0507e..0000000 --- a/drivers/staging/solo6x10/solo6010-jpeg.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __SOLO6010_JPEG_H -#define __SOLO6010_JPEG_H - -static unsigned char jpeg_header[] = { - 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c, - 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, - 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, - 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c, - 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, - 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, 0x3a, 0x50, - 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, 0x70, 0x6e, - 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, - 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, - 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, 0xf2, 0xe0, - 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0xff, 0xdb, - 0x00, 0x43, 0x01, 0x22, 0x24, 0x24, 0x30, 0x2a, - 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6, 0x84, 0x70, - 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xff, 0xc4, 0x01, 0xa2, 0x00, - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, - 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, - 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, - 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, - 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, - 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, - 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, - 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, - 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, - 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, - 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, - 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, - 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, - 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, - 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, - 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, - 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, - 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, - 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, - 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, - 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, - 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, - 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, - 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, - 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, - 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, - 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, - 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, - 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, - 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, - 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, - 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, - 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x02, 0xc0, - 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, - 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, - 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 -}; - -/* This is the byte marker for the start of SOF0: 0xffc0 marker */ -#define SOF0_START 575 - -#endif /* __SOLO6010_JPEG_H */ diff --git a/drivers/staging/solo6x10/solo6010-offsets.h b/drivers/staging/solo6x10/solo6010-offsets.h deleted file mode 100644 index b176003..0000000 --- a/drivers/staging/solo6x10/solo6010-offsets.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __SOLO6010_OFFSETS_H -#define __SOLO6010_OFFSETS_H - -/* Offsets and sizes of the external address */ -#define SOLO_DISP_EXT_ADDR 0x00000000 -#define SOLO_DISP_EXT_SIZE 0x00480000 - -#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) -#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000 - -#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE) -#define SOLO_OSG_EXT_SIZE 0x00120000 - -#define SOLO_EOSD_EXT_ADDR (SOLO_OSG_EXT_ADDR + SOLO_OSG_EXT_SIZE) -#define SOLO_EOSD_EXT_SIZE 0x00010000 - -#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \ - (SOLO_EOSD_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MOTION_EXT_SIZE 0x00080000 - -#define SOLO_G723_EXT_ADDR(__solo) \ - (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE) -#define SOLO_G723_EXT_SIZE 0x00010000 - -#define SOLO_CAP_EXT_ADDR(__solo) \ - (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) -#define SOLO_CAP_EXT_MAX_PAGE (18 + 15) -#define SOLO_CAP_EXT_SIZE (SOLO_CAP_EXT_MAX_PAGE * 65536) - -/* This +1 is very important -- Why?! -- BenC */ -#define SOLO_EREF_EXT_ADDR(__solo) \ - (SOLO_CAP_EXT_ADDR(__solo) + \ - (SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1))) -#define SOLO_EREF_EXT_SIZE 0x00140000 - -#define SOLO_MP4E_EXT_ADDR(__solo) \ - (SOLO_EREF_EXT_ADDR(__solo) + \ - (SOLO_EREF_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) - -#define SOLO_DREF_EXT_ADDR(__solo) \ - (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) -#define SOLO_DREF_EXT_SIZE 0x00140000 - -#define SOLO_MP4D_EXT_ADDR(__solo) \ - (SOLO_DREF_EXT_ADDR(__solo) + \ - (SOLO_DREF_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MP4D_EXT_SIZE 0x00080000 - -#define SOLO_JPEG_EXT_ADDR(__solo) \ - (SOLO_MP4D_EXT_ADDR(__solo) + \ - (SOLO_MP4D_EXT_SIZE * __solo->nr_chans)) -#define SOLO_JPEG_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) - -#endif /* __SOLO6010_OFFSETS_H */ diff --git a/drivers/staging/solo6x10/solo6010-osd-font.h b/drivers/staging/solo6x10/solo6010-osd-font.h deleted file mode 100644 index d72efbb..0000000 --- a/drivers/staging/solo6x10/solo6010-osd-font.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __SOLO6010_OSD_FONT_H -#define __SOLO6010_OSD_FONT_H - -static const unsigned int solo_osd_font[] = { - 0x00000000, 0x0000c0c8, 0xccfefe0c, 0x08000000, - 0x00000000, 0x10103838, 0x7c7cfefe, 0x00000000, /* 0 */ - 0x00000000, 0xfefe7c7c, 0x38381010, 0x10000000, - 0x00000000, 0x7c82fefe, 0xfefefe7c, 0x00000000, - 0x00000000, 0x00001038, 0x10000000, 0x00000000, - 0x00000000, 0x0010387c, 0xfe7c3810, 0x00000000, - 0x00000000, 0x00384444, 0x44380000, 0x00000000, - 0x00000000, 0x38448282, 0x82443800, 0x00000000, - 0x00000000, 0x007c7c7c, 0x7c7c0000, 0x00000000, - 0x00000000, 0x6c6c6c6c, 0x6c6c6c6c, 0x00000000, - 0x00000000, 0x061e7efe, 0xfe7e1e06, 0x00000000, - 0x00000000, 0xc0f0fcfe, 0xfefcf0c0, 0x00000000, - 0x00000000, 0xc6cedefe, 0xfedecec6, 0x00000000, - 0x00000000, 0xc6e6f6fe, 0xfef6e6c6, 0x00000000, - 0x00000000, 0x12367efe, 0xfe7e3612, 0x00000000, - 0x00000000, 0x90d8fcfe, 0xfefcd890, 0x00000000, - 0x00000038, 0x7cc692ba, 0x92c67c38, 0x00000000, - 0x00000038, 0x7cc6aa92, 0xaac67c38, 0x00000000, - 0x00000038, 0x7830107c, 0xbaa8680c, 0x00000000, - 0x00000038, 0x3c18127c, 0xb8382c60, 0x00000000, - 0x00000044, 0xaa6c8254, 0x38eec67c, 0x00000000, - 0x00000082, 0x44288244, 0x38c6827c, 0x00000000, - 0x00000038, 0x444444fe, 0xfeeec6fe, 0x00000000, - 0x00000018, 0x78187818, 0x3c7e7e3c, 0x00000000, - 0x00000000, 0x3854929a, 0x82443800, 0x00000000, - 0x00000000, 0x00c0c8cc, 0xfefe0c08, 0x00000000, - 0x0000e0a0, 0xe040e00e, 0x8a0ea40e, 0x00000000, - 0x0000e0a0, 0xe040e00e, 0x0a8e440e, 0x00000000, - 0x0000007c, 0x82829292, 0x929282fe, 0x00000000, - 0x000000f8, 0xfc046494, 0x946404fc, 0x00000000, - 0x0000003f, 0x7f404c52, 0x524c407f, 0x00000000, - 0x0000007c, 0x82ba82ba, 0x82ba82fe, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x183c3c3c, 0x18180018, 0x18000000, /* 32 ! */ - 0x00000066, 0x66240000, 0x00000000, 0x00000000, - 0x00000000, 0x6c6cfe6c, 0x6c6cfe6c, 0x6c000000, /* 34 " # */ - 0x00001010, 0x7cd6d616, 0x7cd0d6d6, 0x7c101000, - 0x00000000, 0x0086c660, 0x30180cc6, 0xc2000000, /* 36 $ % */ - 0x00000000, 0x386c6c38, 0xdc766666, 0xdc000000, - 0x0000000c, 0x0c0c0600, 0x00000000, 0x00000000, /* 38 & ' */ - 0x00000000, 0x30180c0c, 0x0c0c0c18, 0x30000000, - 0x00000000, 0x0c183030, 0x30303018, 0x0c000000, /* 40 ( ) */ - 0x00000000, 0x0000663c, 0xff3c6600, 0x00000000, - 0x00000000, 0x00001818, 0x7e181800, 0x00000000, /* 42 * + */ - 0x00000000, 0x00000000, 0x00000e0e, 0x0c060000, - 0x00000000, 0x00000000, 0x7e000000, 0x00000000, /* 44 , - */ - 0x00000000, 0x00000000, 0x00000006, 0x06000000, - 0x00000000, 0x80c06030, 0x180c0602, 0x00000000, /* 46 . / */ - 0x0000007c, 0xc6e6f6de, 0xcec6c67c, 0x00000000, - 0x00000030, 0x383c3030, 0x303030fc, 0x00000000, /* 48 0 1 */ - 0x0000007c, 0xc6c06030, 0x180cc6fe, 0x00000000, - 0x0000007c, 0xc6c0c07c, 0xc0c0c67c, 0x00000000, /* 50 2 3 */ - 0x00000060, 0x70786c66, 0xfe6060f0, 0x00000000, - 0x000000fe, 0x0606067e, 0xc0c0c67c, 0x00000000, /* 52 4 5 */ - 0x00000038, 0x0c06067e, 0xc6c6c67c, 0x00000000, - 0x000000fe, 0xc6c06030, 0x18181818, 0x00000000, /* 54 6 7 */ - 0x0000007c, 0xc6c6c67c, 0xc6c6c67c, 0x00000000, - 0x0000007c, 0xc6c6c6fc, 0xc0c06038, 0x00000000, /* 56 8 9 */ - 0x00000000, 0x18180000, 0x00181800, 0x00000000, - 0x00000000, 0x18180000, 0x0018180c, 0x00000000, /* 58 : ; */ - 0x00000060, 0x30180c06, 0x0c183060, 0x00000000, - 0x00000000, 0x007e0000, 0x007e0000, 0x00000000, - 0x00000006, 0x0c183060, 0x30180c06, 0x00000000, - 0x0000007c, 0xc6c66030, 0x30003030, 0x00000000, - 0x0000007c, 0xc6f6d6d6, 0x7606067c, 0x00000000, - 0x00000010, 0x386cc6c6, 0xfec6c6c6, 0x00000000, /* 64 @ A */ - 0x0000007e, 0xc6c6c67e, 0xc6c6c67e, 0x00000000, - 0x00000078, 0xcc060606, 0x0606cc78, 0x00000000, /* 66 */ - 0x0000003e, 0x66c6c6c6, 0xc6c6663e, 0x00000000, - 0x000000fe, 0x0606063e, 0x060606fe, 0x00000000, /* 68 */ - 0x000000fe, 0x0606063e, 0x06060606, 0x00000000, - 0x00000078, 0xcc060606, 0xf6c6ccb8, 0x00000000, /* 70 */ - 0x000000c6, 0xc6c6c6fe, 0xc6c6c6c6, 0x00000000, - 0x0000003c, 0x18181818, 0x1818183c, 0x00000000, /* 72 */ - 0x00000060, 0x60606060, 0x6066663c, 0x00000000, - 0x000000c6, 0xc666361e, 0x3666c6c6, 0x00000000, /* 74 */ - 0x00000006, 0x06060606, 0x060606fe, 0x00000000, - 0x000000c6, 0xeefed6c6, 0xc6c6c6c6, 0x00000000, /* 76 */ - 0x000000c6, 0xcedefef6, 0xe6c6c6c6, 0x00000000, - 0x00000038, 0x6cc6c6c6, 0xc6c66c38, 0x00000000, /* 78 */ - 0x0000007e, 0xc6c6c67e, 0x06060606, 0x00000000, - 0x00000038, 0x6cc6c6c6, 0xc6d67c38, 0x60000000, /* 80 */ - 0x0000007e, 0xc6c6c67e, 0x66c6c6c6, 0x00000000, - 0x0000007c, 0xc6c60c38, 0x60c6c67c, 0x00000000, /* 82 */ - 0x0000007e, 0x18181818, 0x18181818, 0x00000000, - 0x000000c6, 0xc6c6c6c6, 0xc6c6c67c, 0x00000000, /* 84 */ - 0x000000c6, 0xc6c6c6c6, 0xc66c3810, 0x00000000, - 0x000000c6, 0xc6c6c6c6, 0xd6d6fe6c, 0x00000000, /* 86 */ - 0x000000c6, 0xc6c66c38, 0x6cc6c6c6, 0x00000000, - 0x00000066, 0x66666666, 0x3c181818, 0x00000000, /* 88 */ - 0x000000fe, 0xc0603018, 0x0c0606fe, 0x00000000, - 0x0000003c, 0x0c0c0c0c, 0x0c0c0c3c, 0x00000000, /* 90 */ - 0x00000002, 0x060c1830, 0x60c08000, 0x00000000, - 0x0000003c, 0x30303030, 0x3030303c, 0x00000000, /* 92 */ - 0x00001038, 0x6cc60000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00fe0000, - 0x00001818, 0x30000000, 0x00000000, 0x00000000, - 0x00000000, 0x00003c60, 0x7c66667c, 0x00000000, - 0x0000000c, 0x0c0c7ccc, 0xcccccc7c, 0x00000000, - 0x00000000, 0x00007cc6, 0x0606c67c, 0x00000000, - 0x00000060, 0x60607c66, 0x6666667c, 0x00000000, - 0x00000000, 0x00007cc6, 0xfe06c67c, 0x00000000, - 0x00000078, 0x0c0c0c3e, 0x0c0c0c0c, 0x00000000, - 0x00000000, 0x00007c66, 0x6666667c, 0x60603e00, - 0x0000000c, 0x0c0c7ccc, 0xcccccccc, 0x00000000, - 0x00000030, 0x30003830, 0x30303078, 0x00000000, - 0x00000030, 0x30003c30, 0x30303030, 0x30301f00, - 0x0000000c, 0x0c0ccc6c, 0x3c6ccccc, 0x00000000, - 0x00000030, 0x30303030, 0x30303030, 0x00000000, - 0x00000000, 0x000066fe, 0xd6d6d6d6, 0x00000000, - 0x00000000, 0x000078cc, 0xcccccccc, 0x00000000, - 0x00000000, 0x00007cc6, 0xc6c6c67c, 0x00000000, - 0x00000000, 0x00007ccc, 0xcccccc7c, 0x0c0c0c00, - 0x00000000, 0x00007c66, 0x6666667c, 0x60606000, - 0x00000000, 0x000076dc, 0x0c0c0c0c, 0x00000000, - 0x00000000, 0x00007cc6, 0x1c70c67c, 0x00000000, - 0x00000000, 0x1818fe18, 0x18181870, 0x00000000, - 0x00000000, 0x00006666, 0x6666663c, 0x00000000, - 0x00000000, 0x0000c6c6, 0xc66c3810, 0x00000000, - 0x00000000, 0x0000c6d6, 0xd6d6fe6c, 0x00000000, - 0x00000000, 0x0000c66c, 0x38386cc6, 0x00000000, - 0x00000000, 0x00006666, 0x6666667c, 0x60603e00, - 0x00000000, 0x0000fe60, 0x30180cfe, 0x00000000, - 0x00000070, 0x1818180e, 0x18181870, 0x00000000, - 0x00000018, 0x18181800, 0x18181818, 0x00000000, - 0x0000000e, 0x18181870, 0x1818180e, 0x00000000, - 0x000000dc, 0x76000000, 0x00000000, 0x00000000, - 0x00000000, 0x0010386c, 0xc6c6fe00, 0x00000000 -}; - -#endif /* __SOLO6010_OSD_FONT_H */ diff --git a/drivers/staging/solo6x10/solo6010-p2m.c b/drivers/staging/solo6x10/solo6010-p2m.c deleted file mode 100644 index 90de963..0000000 --- a/drivers/staging/solo6x10/solo6010-p2m.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include - -#include "solo6010.h" - -/* #define SOLO_TEST_P2M */ - -int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, - void *sys_addr, u32 ext_addr, u32 size) -{ - dma_addr_t dma_addr; - int ret; - - WARN_ON(!size); - BUG_ON(id >= SOLO_NR_P2M); - - if (!size) - return -EINVAL; - - dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size); - - pci_unmap_single(solo_dev->pdev, dma_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - return ret; -} - -int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size) -{ - struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA); - int ret; - - if (desc == NULL) - return -ENOMEM; - - solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0); - ret = solo_p2m_dma_desc(solo_dev, id, desc, 2); - kfree(desc); - - return ret; -} - -void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, - u32 ext_addr, u32 size, int repeat, u32 ext_size) -{ - desc->ta = cpu_to_le32(dma_addr); - desc->fa = cpu_to_le32(ext_addr); - - desc->ext = cpu_to_le32(SOLO_P2M_COPY_SIZE(size >> 2)); - desc->ctrl = cpu_to_le32(SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | - (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON); - - /* Ext size only matters when we're repeating */ - if (repeat) { - desc->ext |= cpu_to_le32(SOLO_P2M_EXT_INC(ext_size >> 2)); - desc->ctrl |= cpu_to_le32(SOLO_P2M_PCI_INC(size >> 2) | - SOLO_P2M_REPEAT(repeat)); - } -} - -int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id, - struct p2m_desc *desc, int desc_count) -{ - struct solo_p2m_dev *p2m_dev; - unsigned int timeout; - int ret = 0; - u32 config = 0; - dma_addr_t desc_dma = 0; - - BUG_ON(id >= SOLO_NR_P2M); - BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC); - - p2m_dev = &solo_dev->p2m_dev[id]; - - mutex_lock(&p2m_dev->mutex); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); - - INIT_COMPLETION(p2m_dev->completion); - p2m_dev->error = 0; - - /* Enable the descriptors */ - config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id)); - desc_dma = pci_map_single(solo_dev->pdev, desc, - desc_count * sizeof(*desc), - PCI_DMA_TODEVICE); - solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma); - solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config | - SOLO_P2M_DESC_MODE); - - /* Should have all descriptors completed from one interrupt */ - timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); - - /* Reset back to non-descriptor mode */ - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config); - solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0); - solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0); - pci_unmap_single(solo_dev->pdev, desc_dma, - desc_count * sizeof(*desc), - PCI_DMA_TODEVICE); - - if (p2m_dev->error) - ret = -EIO; - else if (timeout == 0) - ret = -EAGAIN; - - mutex_unlock(&p2m_dev->mutex); - - WARN_ON_ONCE(ret); - - return ret; -} - -int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id, - struct p2m_desc *pdesc, int wr, - struct scatterlist *sg, u32 sg_off, - u32 ext_addr, u32 size) -{ - int i; - int idx; - - BUG_ON(id >= SOLO_NR_P2M); - - if (WARN_ON_ONCE(!size)) - return -EINVAL; - - memset(pdesc, 0, sizeof(*pdesc)); - - /* Should rewrite this to handle > SOLO_NR_P2M_DESC transactions */ - for (i = 0, idx = 1; idx < SOLO_NR_P2M_DESC && sg && size > 0; - i++, sg = sg_next(sg)) { - struct p2m_desc *desc = &pdesc[idx]; - u32 sg_len = sg_dma_len(sg); - u32 len; - - if (sg_off >= sg_len) { - sg_off -= sg_len; - continue; - } - - sg_len -= sg_off; - len = min(sg_len, size); - - solo_p2m_push_desc(desc, wr, sg_dma_address(sg) + sg_off, - ext_addr, len, 0, 0); - - size -= len; - ext_addr += len; - idx++; - - sg_off = 0; - } - - WARN_ON_ONCE(size || i >= SOLO_NR_P2M_DESC); - - return solo_p2m_dma_desc(solo_dev, id, pdesc, idx); -} - -#ifdef SOLO_TEST_P2M - -#define P2M_TEST_CHAR 0xbe - -static unsigned long long p2m_test(struct solo6010_dev *solo_dev, u8 id, - u32 base, int size) -{ - u8 *wr_buf; - u8 *rd_buf; - int i; - unsigned long long err_cnt = 0; - - wr_buf = kmalloc(size, GFP_KERNEL); - if (!wr_buf) { - printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n"); - return size; - } - - rd_buf = kmalloc(size, GFP_KERNEL); - if (!rd_buf) { - printk(SOLO6010_NAME ": Failed to malloc for p2m_test\n"); - kfree(wr_buf); - return size; - } - - memset(wr_buf, P2M_TEST_CHAR, size); - memset(rd_buf, P2M_TEST_CHAR + 1, size); - - solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size); - solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size); - - for (i = 0; i < size; i++) - if (wr_buf[i] != rd_buf[i]) - err_cnt++; - - kfree(wr_buf); - kfree(rd_buf); - - return err_cnt; -} - -#define TEST_CHUNK_SIZE (8 * 1024) - -static void run_p2m_test(struct solo6010_dev *solo_dev) -{ - unsigned long long errs = 0; - u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); - int i, d; - - printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", - SOLO6010_NAME, size); - - for (i = 0; i < size; i += TEST_CHUNK_SIZE) - for (d = 0; d < 4; d++) - errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); - - printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", - SOLO6010_NAME, errs); - - return; -} -#else -#define run_p2m_test(__solo) do {} while (0) -#endif - -void solo_p2m_isr(struct solo6010_dev *solo_dev, int id) -{ - struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id)); - - complete(&p2m_dev->completion); -} - -void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status) -{ - struct solo_p2m_dev *p2m_dev; - int i; - - if (!(status & SOLO_PCI_ERR_P2M)) - return; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - p2m_dev->error = 1; - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - complete(&p2m_dev->completion); - } -} - -void solo_p2m_exit(struct solo6010_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) - solo6010_irq_off(solo_dev, SOLO_IRQ_P2M(i)); -} - -int solo_p2m_init(struct solo6010_dev *solo_dev) -{ - struct solo_p2m_dev *p2m_dev; - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - - mutex_init(&p2m_dev->mutex); - init_completion(&p2m_dev->completion); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), - SOLO_P2M_CSC_16BIT_565 | - SOLO_P2M_DMA_INTERVAL(3) | - SOLO_P2M_DESC_INTR_OPT | - SOLO_P2M_PCI_MASTER_MODE); - solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i)); - } - - run_p2m_test(solo_dev); - - return 0; -} diff --git a/drivers/staging/solo6x10/solo6010-registers.h b/drivers/staging/solo6x10/solo6010-registers.h deleted file mode 100644 index db291bf..0000000 --- a/drivers/staging/solo6x10/solo6010-registers.h +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __SOLO6010_REGISTERS_H -#define __SOLO6010_REGISTERS_H - -#include "solo6010-offsets.h" - -/* Global 6010 system configuration */ -#define SOLO_SYS_CFG 0x0000 -#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */ -#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */ -#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */ -#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */ -#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */ -#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */ -#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000 -#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24) -#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26) -#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 /* 6110: must be set */ -#define SOLO_SYS_CFG_RESET 0x80000000 - -#define SOLO_DMA_CTRL 0x0004 -#define SOLO_DMA_CTRL_REFRESH_CYCLE(n) ((n)<<8) -/* 0=16/32MB, 1=32/64MB, 2=64/128MB, 3=128/256MB */ -#define SOLO_DMA_CTRL_SDRAM_SIZE(n) ((n)<<6) -#define SOLO_DMA_CTRL_SDRAM_CLK_INVERT (1<<5) -#define SOLO_DMA_CTRL_STROBE_SELECT (1<<4) -#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3) -#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2) -#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0) -#define SOLO_DMA_CTRL1 0x0008 - -#define SOLO_SYS_VCLK 0x000C -#define SOLO_VCLK_INVERT (1<<22) -/* 0=sys_clk/4, 1=sys_clk/2, 2=clk_in/2 of system input */ -#define SOLO_VCLK_SELECT(n) ((n)<<20) -#define SOLO_VCLK_VIN1415_DELAY(n) ((n)<<14) -#define SOLO_VCLK_VIN1213_DELAY(n) ((n)<<12) -#define SOLO_VCLK_VIN1011_DELAY(n) ((n)<<10) -#define SOLO_VCLK_VIN0809_DELAY(n) ((n)<<8) -#define SOLO_VCLK_VIN0607_DELAY(n) ((n)<<6) -#define SOLO_VCLK_VIN0405_DELAY(n) ((n)<<4) -#define SOLO_VCLK_VIN0203_DELAY(n) ((n)<<2) -#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0) - -#define SOLO_IRQ_STAT 0x0010 -#define SOLO_IRQ_ENABLE 0x0014 -#define SOLO_IRQ_P2M(n) (1<<((n)+17)) -#define SOLO_IRQ_GPIO (1<<16) -#define SOLO_IRQ_VIDEO_LOSS (1<<15) -#define SOLO_IRQ_VIDEO_IN (1<<14) -#define SOLO_IRQ_MOTION (1<<13) -#define SOLO_IRQ_ATA_CMD (1<<12) -#define SOLO_IRQ_ATA_DIR (1<<11) -#define SOLO_IRQ_PCI_ERR (1<<10) -#define SOLO_IRQ_PS2_1 (1<<9) -#define SOLO_IRQ_PS2_0 (1<<8) -#define SOLO_IRQ_SPI (1<<7) -#define SOLO_IRQ_IIC (1<<6) -#define SOLO_IRQ_UART(n) (1<<((n) + 4)) -#define SOLO_IRQ_G723 (1<<3) -#define SOLO_IRQ_DECODER (1<<1) -#define SOLO_IRQ_ENCODER (1<<0) - -#define SOLO_CHIP_OPTION 0x001C -#define SOLO_CHIP_ID_MASK 0x00000007 - -#define SOLO6110_PLL_CONFIG 0x0020 -#define SOLO6110_PLL_RANGE_BYPASS (0 << 20) -#define SOLO6110_PLL_RANGE_5_10MHZ (1 << 20) -#define SOLO6110_PLL_RANGE_8_16MHZ (2 << 20) -#define SOLO6110_PLL_RANGE_13_26MHZ (3 << 20) -#define SOLO6110_PLL_RANGE_21_42MHZ (4 << 20) -#define SOLO6110_PLL_RANGE_34_68MHZ (5 << 20) -#define SOLO6110_PLL_RANGE_54_108MHZ (6 << 20) -#define SOLO6110_PLL_RANGE_88_200MHZ (7 << 20) -#define SOLO6110_PLL_DIVR(x) (((x) - 1) << 15) -#define SOLO6110_PLL_DIVQ_EXP(x) ((x) << 12) -#define SOLO6110_PLL_DIVF(x) (((x) - 1) << 4) -#define SOLO6110_PLL_RESET (1 << 3) -#define SOLO6110_PLL_BYPASS (1 << 2) -#define SOLO6110_PLL_FSEN (1 << 1) -#define SOLO6110_PLL_FB (1 << 0) - -#define SOLO_EEPROM_CTRL 0x0060 -#define SOLO_EEPROM_ACCESS_EN (1<<7) -#define SOLO_EEPROM_CS (1<<3) -#define SOLO_EEPROM_CLK (1<<2) -#define SOLO_EEPROM_DO (1<<1) -#define SOLO_EEPROM_DI (1<<0) -#define SOLO_EEPROM_ENABLE (EEPROM_ACCESS_EN | EEPROM_CS) - -#define SOLO_PCI_ERR 0x0070 -#define SOLO_PCI_ERR_FATAL 0x00000001 -#define SOLO_PCI_ERR_PARITY 0x00000002 -#define SOLO_PCI_ERR_TARGET 0x00000004 -#define SOLO_PCI_ERR_TIMEOUT 0x00000008 -#define SOLO_PCI_ERR_P2M 0x00000010 -#define SOLO_PCI_ERR_ATA 0x00000020 -#define SOLO_PCI_ERR_P2M_DESC 0x00000040 -#define SOLO_PCI_ERR_FSM0(__s) (((__s) >> 16) & 0x0f) -#define SOLO_PCI_ERR_FSM1(__s) (((__s) >> 20) & 0x0f) -#define SOLO_PCI_ERR_FSM2(__s) (((__s) >> 24) & 0x1f) - -#define SOLO_P2M_BASE 0x0080 - -#define SOLO_P2M_CONFIG(n) (0x0080 + ((n)*0x20)) -#define SOLO_P2M_DMA_INTERVAL(n) ((n)<<6)/* N*32 clocks */ -#define SOLO_P2M_CSC_BYTE_REORDER (1<<5) /* BGR -> RGB */ -/* 0:r=[14:10] g=[9:5] b=[4:0], 1:r=[15:11] g=[10:5] b=[4:0] */ -#define SOLO_P2M_CSC_16BIT_565 (1<<4) -#define SOLO_P2M_UV_SWAP (1<<3) -#define SOLO_P2M_PCI_MASTER_MODE (1<<2) -#define SOLO_P2M_DESC_INTR_OPT (1<<1) /* 1:Empty, 0:Each */ -#define SOLO_P2M_DESC_MODE (1<<0) - -#define SOLO_P2M_DES_ADR(n) (0x0084 + ((n)*0x20)) - -#define SOLO_P2M_DESC_ID(n) (0x0088 + ((n)*0x20)) -#define SOLO_P2M_UPDATE_ID(n) ((n)<<0) - -#define SOLO_P2M_STATUS(n) (0x008C + ((n)*0x20)) -#define SOLO_P2M_COMMAND_DONE (1<<8) -#define SOLO_P2M_CURRENT_ID(stat) (0xff & (stat)) - -#define SOLO_P2M_CONTROL(n) (0x0090 + ((n)*0x20)) -#define SOLO_P2M_PCI_INC(n) ((n)<<20) -#define SOLO_P2M_REPEAT(n) ((n)<<10) -/* 0:512, 1:256, 2:128, 3:64, 4:32, 5:128(2page) */ -#define SOLO_P2M_BURST_SIZE(n) ((n)<<7) -#define SOLO_P2M_BURST_512 0 -#define SOLO_P2M_BURST_256 1 -#define SOLO_P2M_BURST_128 2 -#define SOLO_P2M_BURST_64 3 -#define SOLO_P2M_BURST_32 4 -#define SOLO_P2M_CSC_16BIT (1<<6) /* 0:24bit, 1:16bit */ -/* 0:Y[0]<-0(OFF), 1:Y[0]<-1(ON), 2:Y[0]<-G[0], 3:Y[0]<-Bit[15] */ -#define SOLO_P2M_ALPHA_MODE(n) ((n)<<4) -#define SOLO_P2M_CSC_ON (1<<3) -#define SOLO_P2M_INTERRUPT_REQ (1<<2) -#define SOLO_P2M_WRITE (1<<1) -#define SOLO_P2M_TRANS_ON (1<<0) - -#define SOLO_P2M_EXT_CFG(n) (0x0094 + ((n)*0x20)) -#define SOLO_P2M_EXT_INC(n) ((n)<<20) -#define SOLO_P2M_COPY_SIZE(n) ((n)<<0) - -#define SOLO_P2M_TAR_ADR(n) (0x0098 + ((n)*0x20)) - -#define SOLO_P2M_EXT_ADR(n) (0x009C + ((n)*0x20)) - -#define SOLO_P2M_BUFFER(i) (0x2000 + ((i)*4)) - -#define SOLO_VI_CH_SWITCH_0 0x0100 -#define SOLO_VI_CH_SWITCH_1 0x0104 -#define SOLO_VI_CH_SWITCH_2 0x0108 - -#define SOLO_VI_CH_ENA 0x010C -#define SOLO_VI_CH_FORMAT 0x0110 -#define SOLO_VI_FD_SEL_MASK(n) ((n)<<16) -#define SOLO_VI_PROG_MASK(n) ((n)<<0) - -#define SOLO_VI_FMT_CFG 0x0114 -#define SOLO_VI_FMT_CHECK_VCOUNT (1<<31) -#define SOLO_VI_FMT_CHECK_HCOUNT (1<<30) -#define SOLO_VI_FMT_TEST_SIGNAL (1<<28) - -#define SOLO_VI_PAGE_SW 0x0118 -#define SOLO_FI_INV_DISP_LIVE(n) ((n)<<8) -#define SOLO_FI_INV_DISP_OUT(n) ((n)<<7) -#define SOLO_DISP_SYNC_FI(n) ((n)<<6) -#define SOLO_PIP_PAGE_ADD(n) ((n)<<3) -#define SOLO_NORMAL_PAGE_ADD(n) ((n)<<0) - -#define SOLO_VI_ACT_I_P 0x011C -#define SOLO_VI_ACT_I_S 0x0120 -#define SOLO_VI_ACT_P 0x0124 -#define SOLO_VI_FI_INVERT (1<<31) -#define SOLO_VI_H_START(n) ((n)<<21) -#define SOLO_VI_V_START(n) ((n)<<11) -#define SOLO_VI_V_STOP(n) ((n)<<0) - -#define SOLO_VI_STATUS0 0x0128 -#define SOLO_VI_STATUS0_PAGE(__n) ((__n) & 0x07) -#define SOLO_VI_STATUS1 0x012C - -/* XXX: Might be better off in kernel level disp.h */ -#define DISP_PAGE(stat) ((stat) & 0x07) - -#define SOLO_VI_PB_CONFIG 0x0130 -#define SOLO_VI_PB_USER_MODE (1<<1) -#define SOLO_VI_PB_PAL (1<<0) -#define SOLO_VI_PB_RANGE_HV 0x0134 -#define SOLO_VI_PB_HSIZE(h) ((h)<<12) -#define SOLO_VI_PB_VSIZE(v) ((v)<<0) -#define SOLO_VI_PB_ACT_H 0x0138 -#define SOLO_VI_PB_HSTART(n) ((n)<<12) -#define SOLO_VI_PB_HSTOP(n) ((n)<<0) -#define SOLO_VI_PB_ACT_V 0x013C -#define SOLO_VI_PB_VSTART(n) ((n)<<12) -#define SOLO_VI_PB_VSTOP(n) ((n)<<0) - -#define SOLO_VI_MOSAIC(ch) (0x0140 + ((ch)*4)) -#define SOLO_VI_MOSAIC_SX(x) ((x)<<24) -#define SOLO_VI_MOSAIC_EX(x) ((x)<<16) -#define SOLO_VI_MOSAIC_SY(x) ((x)<<8) -#define SOLO_VI_MOSAIC_EY(x) ((x)<<0) - -#define SOLO_VI_WIN_CTRL0(ch) (0x0180 + ((ch)*4)) -#define SOLO_VI_WIN_CTRL1(ch) (0x01C0 + ((ch)*4)) - -#define SOLO_VI_WIN_CHANNEL(n) ((n)<<28) - -#define SOLO_VI_WIN_PIP(n) ((n)<<27) -#define SOLO_VI_WIN_SCALE(n) ((n)<<24) - -#define SOLO_VI_WIN_SX(x) ((x)<<12) -#define SOLO_VI_WIN_EX(x) ((x)<<0) - -#define SOLO_VI_WIN_SY(x) ((x)<<12) -#define SOLO_VI_WIN_EY(x) ((x)<<0) - -#define SOLO_VI_WIN_ON(ch) (0x0200 + ((ch)*4)) - -#define SOLO_VI_WIN_SW 0x0240 -#define SOLO_VI_WIN_LIVE_AUTO_MUTE 0x0244 - -#define SOLO_VI_MOT_ADR 0x0260 -#define SOLO_VI_MOTION_EN(mask) ((mask)<<16) -#define SOLO_VI_MOT_CTRL 0x0264 -#define SOLO_VI_MOTION_FRAME_COUNT(n) ((n)<<24) -#define SOLO_VI_MOTION_SAMPLE_LENGTH(n) ((n)<<16) -#define SOLO_VI_MOTION_INTR_START_STOP (1<<15) -#define SOLO_VI_MOTION_FREEZE_DATA (1<<14) -#define SOLO_VI_MOTION_SAMPLE_COUNT(n) ((n)<<0) -#define SOLO_VI_MOT_CLEAR 0x0268 -#define SOLO_VI_MOT_STATUS 0x026C -#define SOLO_VI_MOTION_CNT(n) ((n)<<0) -#define SOLO_VI_MOTION_BORDER 0x0270 -#define SOLO_VI_MOTION_BAR 0x0274 -#define SOLO_VI_MOTION_Y_SET (1<<29) -#define SOLO_VI_MOTION_Y_ADD (1<<28) -#define SOLO_VI_MOTION_CB_SET (1<<27) -#define SOLO_VI_MOTION_CB_ADD (1<<26) -#define SOLO_VI_MOTION_CR_SET (1<<25) -#define SOLO_VI_MOTION_CR_ADD (1<<24) -#define SOLO_VI_MOTION_Y_VALUE(v) ((v)<<16) -#define SOLO_VI_MOTION_CB_VALUE(v) ((v)<<8) -#define SOLO_VI_MOTION_CR_VALUE(v) ((v)<<0) - -#define SOLO_VO_FMT_ENC 0x0300 -#define SOLO_VO_SCAN_MODE_PROGRESSIVE (1<<31) -#define SOLO_VO_FMT_TYPE_PAL (1<<30) -#define SOLO_VO_FMT_TYPE_NTSC 0 -#define SOLO_VO_USER_SET (1<<29) - -#define SOLO_VO_FI_CHANGE (1<<20) -#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19) -#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18) -#define SOLO_VO_USER_COLOR_SET_NAV (1<<17) -#define SOLO_VO_USER_COLOR_SET_NAH (1<<16) -#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8) -#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4) -#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0) - -#define SOLO_VO_ACT_H 0x0304 -#define SOLO_VO_H_BLANK(n) ((n)<<22) -#define SOLO_VO_H_START(n) ((n)<<11) -#define SOLO_VO_H_STOP(n) ((n)<<0) - -#define SOLO_VO_ACT_V 0x0308 -#define SOLO_VO_V_BLANK(n) ((n)<<22) -#define SOLO_VO_V_START(n) ((n)<<11) -#define SOLO_VO_V_STOP(n) ((n)<<0) - -#define SOLO_VO_RANGE_HV 0x030C -#define SOLO_VO_SYNC_INVERT (1<<24) -#define SOLO_VO_HSYNC_INVERT (1<<23) -#define SOLO_VO_VSYNC_INVERT (1<<22) -#define SOLO_VO_H_LEN(n) ((n)<<11) -#define SOLO_VO_V_LEN(n) ((n)<<0) - -#define SOLO_VO_DISP_CTRL 0x0310 -#define SOLO_VO_DISP_ON (1<<31) -#define SOLO_VO_DISP_ERASE_COUNT(n) ((n&0xf)<<24) -#define SOLO_VO_DISP_DOUBLE_SCAN (1<<22) -#define SOLO_VO_DISP_SINGLE_PAGE (1<<21) -#define SOLO_VO_DISP_BASE(n) (((n)>>16) & 0xffff) - -#define SOLO_VO_DISP_ERASE 0x0314 -#define SOLO_VO_DISP_ERASE_ON (1<<0) - -#define SOLO_VO_ZOOM_CTRL 0x0318 -#define SOLO_VO_ZOOM_VER_ON (1<<24) -#define SOLO_VO_ZOOM_HOR_ON (1<<23) -#define SOLO_VO_ZOOM_V_COMP (1<<22) -#define SOLO_VO_ZOOM_SX(h) (((h)/2)<<11) -#define SOLO_VO_ZOOM_SY(v) (((v)/2)<<0) - -#define SOLO_VO_FREEZE_CTRL 0x031C -#define SOLO_VO_FREEZE_ON (1<<1) -#define SOLO_VO_FREEZE_INTERPOLATION (1<<0) - -#define SOLO_VO_BKG_COLOR 0x0320 -#define SOLO_BG_Y(y) ((y)<<16) -#define SOLO_BG_U(u) ((u)<<8) -#define SOLO_BG_V(v) ((v)<<0) - -#define SOLO_VO_DEINTERLACE 0x0324 -#define SOLO_VO_DEINTERLACE_THRESHOLD(n) ((n)<<8) -#define SOLO_VO_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) - -#define SOLO_VO_BORDER_LINE_COLOR 0x0330 -#define SOLO_VO_BORDER_FILL_COLOR 0x0334 -#define SOLO_VO_BORDER_LINE_MASK 0x0338 -#define SOLO_VO_BORDER_FILL_MASK 0x033c - -#define SOLO_VO_BORDER_X(n) (0x0340+((n)*4)) -#define SOLO_VO_BORDER_Y(n) (0x0354+((n)*4)) - -#define SOLO_VO_CELL_EXT_SET 0x0368 -#define SOLO_VO_CELL_EXT_START 0x036c -#define SOLO_VO_CELL_EXT_STOP 0x0370 - -#define SOLO_VO_CELL_EXT_SET2 0x0374 -#define SOLO_VO_CELL_EXT_START2 0x0378 -#define SOLO_VO_CELL_EXT_STOP2 0x037c - -#define SOLO_VO_RECTANGLE_CTRL(n) (0x0368+((n)*12)) -#define SOLO_VO_RECTANGLE_START(n) (0x036c+((n)*12)) -#define SOLO_VO_RECTANGLE_STOP(n) (0x0370+((n)*12)) - -#define SOLO_VO_CURSOR_POS (0x0380) -#define SOLO_VO_CURSOR_CLR (0x0384) -#define SOLO_VO_CURSOR_CLR2 (0x0388) -#define SOLO_VO_CURSOR_MASK(id) (0x0390+((id)*4)) - -#define SOLO_VO_EXPANSION(id) (0x0250+((id)*4)) - -#define SOLO_OSG_CONFIG 0x03E0 -#define SOLO_VO_OSG_ON (1<<31) -#define SOLO_VO_OSG_COLOR_MUTE (1<<28) -#define SOLO_VO_OSG_ALPHA_RATE(n) ((n)<<22) -#define SOLO_VO_OSG_ALPHA_BG_RATE(n) ((n)<<16) -#define SOLO_VO_OSG_BASE(offset) (((offset)>>16)&0xffff) - -#define SOLO_OSG_ERASE 0x03E4 -#define SOLO_OSG_ERASE_ON (0x80) -#define SOLO_OSG_ERASE_OFF (0x00) - -#define SOLO_VO_OSG_BLINK 0x03E8 -#define SOLO_VO_OSG_BLINK_ON (1<<1) -#define SOLO_VO_OSG_BLINK_INTREVAL18 (1<<0) - -#define SOLO_CAP_BASE 0x0400 -#define SOLO_CAP_MAX_PAGE(n) ((n)<<16) -#define SOLO_CAP_BASE_ADDR(n) ((n)<<0) -#define SOLO_CAP_BTW 0x0404 -#define SOLO_CAP_PROG_BANDWIDTH(n) ((n)<<8) -#define SOLO_CAP_MAX_BANDWIDTH(n) ((n)<<0) - -#define SOLO_DIM_SCALE1 0x0408 -#define SOLO_DIM_SCALE2 0x040C -#define SOLO_DIM_SCALE3 0x0410 -#define SOLO_DIM_SCALE4 0x0414 -#define SOLO_DIM_SCALE5 0x0418 -#define SOLO_DIM_V_MB_NUM_FRAME(n) ((n)<<16) -#define SOLO_DIM_V_MB_NUM_FIELD(n) ((n)<<8) -#define SOLO_DIM_H_MB_NUM(n) ((n)<<0) - -#define SOLO_DIM_PROG 0x041C -#define SOLO_CAP_STATUS 0x0420 - -#define SOLO_CAP_CH_SCALE(ch) (0x0440+((ch)*4)) -#define SOLO_CAP_CH_COMP_ENA_E(ch) (0x0480+((ch)*4)) -#define SOLO_CAP_CH_INTV(ch) (0x04C0+((ch)*4)) -#define SOLO_CAP_CH_INTV_E(ch) (0x0500+((ch)*4)) - - -#define SOLO_VE_CFG0 0x0610 -#define SOLO_VE_TWO_PAGE_MODE (1<<31) -#define SOLO_VE_INTR_CTRL(n) ((n)<<24) -#define SOLO_VE_BLOCK_SIZE(n) ((n)<<16) -#define SOLO_VE_BLOCK_BASE(n) ((n)<<0) - -#define SOLO_VE_CFG1 0x0614 -#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */ -#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */ -#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */ -#define SOLO_VE_INSERT_INDEX (1<<18) -#define SOLO_VE_MOTION_MODE(n) ((n)<<16) -#define SOLO_VE_MOTION_BASE(n) ((n)<<0) - -#define SOLO_VE_WMRK_POLY 0x061C -#define SOLO_VE_VMRK_INIT_KEY 0x0620 -#define SOLO_VE_WMRK_STRL 0x0624 -#define SOLO_VE_ENCRYP_POLY 0x0628 -#define SOLO_VE_ENCRYP_INIT 0x062C -#define SOLO_VE_ATTR 0x0630 -#define SOLO_VE_LITTLE_ENDIAN (1<<31) -#define SOLO_COMP_ATTR_RN (1<<30) -#define SOLO_COMP_ATTR_FCODE(n) ((n)<<27) -#define SOLO_COMP_TIME_INC(n) ((n)<<25) -#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21) -#define SOLO_DCT_INTERVAL(n) ((n)<<16) - -#define SOLO_VE_STATE(n) (0x0640+((n)*4)) - -#define SOLO_VE_JPEG_QP_TBL 0x0670 -#define SOLO_VE_JPEG_QP_CH_L 0x0674 -#define SOLO_VE_JPEG_QP_CH_H 0x0678 -#define SOLO_VE_JPEG_CFG 0x067C -#define SOLO_VE_JPEG_CTRL 0x0680 - -#define SOLO_VE_OSD_CH 0x0690 -#define SOLO_VE_OSD_BASE 0x0694 -#define SOLO_VE_OSD_CLR 0x0698 -#define SOLO_VE_OSD_OPT 0x069C - -#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4)) -#define SOLO6010_VE_CH_MOT(ch) (0x0740+((ch)*4)) /* 6010 only */ -#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4)) -#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4)) -#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4)) -#define SOLO_VE_CH_GOP_E(ch) (0x0840+((ch)*4)) -#define SOLO_VE_CH_REF_BASE(ch) (0x0880+((ch)*4)) -#define SOLO_VE_CH_REF_BASE_E(ch) (0x08C0+((ch)*4)) - -#define SOLO_VE_MPEG4_QUE(n) (0x0A00+((n)*8)) -#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8)) - -#define SOLO_VD_CFG0 0x0900 -#define SOLO6010_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) /* 6010 only */ -#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23) -#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22) -#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21) -#define SOLO_VD_CFG_BUSY_WIAT_MS (1<<20) -#define SOLO_VD_CFG_SINGLE_MODE (1<<18) -#define SOLO_VD_CFG_SCAL_MANUAL (1<<17) -#define SOLO_VD_CFG_USER_PAGE_CTRL (1<<16) -#define SOLO_VD_CFG_LITTLE_ENDIAN (1<<15) -#define SOLO_VD_CFG_START_FI (1<<14) -#define SOLO_VD_CFG_ERR_LOCK (1<<13) -#define SOLO_VD_CFG_ERR_INT_ENA (1<<12) -#define SOLO_VD_CFG_TIME_WIDTH(n) ((n)<<8) -#define SOLO_VD_CFG_DCT_INTERVAL(n) ((n)<<0) - -#define SOLO_VD_CFG1 0x0904 - -#define SOLO_VD_DEINTERLACE 0x0908 -#define SOLO_VD_DEINTERLACE_THRESHOLD(n) ((n)<<8) -#define SOLO_VD_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) - -#define SOLO_VD_CODE_ADR 0x090C - -#define SOLO_VD_CTRL 0x0910 -#define SOLO_VD_OPER_ON (1<<31) -#define SOLO_VD_MAX_ITEM(n) ((n)<<0) - -#define SOLO_VD_STATUS0 0x0920 -#define SOLO_VD_STATUS0_INTR_ACK (1<<22) -#define SOLO_VD_STATUS0_INTR_EMPTY (1<<21) -#define SOLO_VD_STATUS0_INTR_ERR (1<<20) - -#define SOLO_VD_STATUS1 0x0924 - -#define SOLO_VD_IDX0 0x0930 -#define SOLO_VD_IDX_INTERLACE (1<<30) -#define SOLO_VD_IDX_CHANNEL(n) ((n)<<24) -#define SOLO_VD_IDX_SIZE(n) ((n)<<0) - -#define SOLO_VD_IDX1 0x0934 -#define SOLO_VD_IDX_SRC_SCALE(n) ((n)<<28) -#define SOLO_VD_IDX_WINDOW(n) ((n)<<24) -#define SOLO_VD_IDX_DEINTERLACE (1<<16) -#define SOLO_VD_IDX_H_BLOCK(n) ((n)<<8) -#define SOLO_VD_IDX_V_BLOCK(n) ((n)<<0) - -#define SOLO_VD_IDX2 0x0938 -#define SOLO_VD_IDX_REF_BASE_SIDE (1<<31) -#define SOLO_VD_IDX_REF_BASE(n) (((n)>>16)&0xffff) - -#define SOLO_VD_IDX3 0x093C -#define SOLO_VD_IDX_DISP_SCALE(n) ((n)<<28) -#define SOLO_VD_IDX_INTERLACE_WR (1<<27) -#define SOLO_VD_IDX_INTERPOL (1<<26) -#define SOLO_VD_IDX_HOR2X (1<<25) -#define SOLO_VD_IDX_OFFSET_X(n) ((n)<<12) -#define SOLO_VD_IDX_OFFSET_Y(n) ((n)<<0) - -#define SOLO_VD_IDX4 0x0940 -#define SOLO_VD_IDX_DEC_WR_PAGE(n) ((n)<<8) -#define SOLO_VD_IDX_DISP_RD_PAGE(n) ((n)<<0) - -#define SOLO_VD_WR_PAGE(n) (0x03F0 + ((n) * 4)) - - -#define SOLO_GPIO_CONFIG_0 0x0B00 -#define SOLO_GPIO_CONFIG_1 0x0B04 -#define SOLO_GPIO_DATA_OUT 0x0B08 -#define SOLO_GPIO_DATA_IN 0x0B0C -#define SOLO_GPIO_INT_ACK_STA 0x0B10 -#define SOLO_GPIO_INT_ENA 0x0B14 -#define SOLO_GPIO_INT_CFG_0 0x0B18 -#define SOLO_GPIO_INT_CFG_1 0x0B1C - - -#define SOLO_IIC_CFG 0x0B20 -#define SOLO_IIC_ENABLE (1<<8) -#define SOLO_IIC_PRESCALE(n) ((n)<<0) - -#define SOLO_IIC_CTRL 0x0B24 -#define SOLO_IIC_AUTO_CLEAR (1<<20) -#define SOLO_IIC_STATE_RX_ACK (1<<19) -#define SOLO_IIC_STATE_BUSY (1<<18) -#define SOLO_IIC_STATE_SIG_ERR (1<<17) -#define SOLO_IIC_STATE_TRNS (1<<16) -#define SOLO_IIC_CH_SET(n) ((n)<<5) -#define SOLO_IIC_ACK_EN (1<<4) -#define SOLO_IIC_START (1<<3) -#define SOLO_IIC_STOP (1<<2) -#define SOLO_IIC_READ (1<<1) -#define SOLO_IIC_WRITE (1<<0) - -#define SOLO_IIC_TXD 0x0B28 -#define SOLO_IIC_RXD 0x0B2C - -/* - * UART REGISTER - */ -#define SOLO_UART_CONTROL(n) (0x0BA0 + ((n)*0x20)) -#define SOLO_UART_CLK_DIV(n) ((n)<<24) -#define SOLO_MODEM_CTRL_EN (1<<20) -#define SOLO_PARITY_ERROR_DROP (1<<18) -#define SOLO_IRQ_ERR_EN (1<<17) -#define SOLO_IRQ_RX_EN (1<<16) -#define SOLO_IRQ_TX_EN (1<<15) -#define SOLO_RX_EN (1<<14) -#define SOLO_TX_EN (1<<13) -#define SOLO_UART_HALF_DUPLEX (1<<12) -#define SOLO_UART_LOOPBACK (1<<11) - -#define SOLO_BAUDRATE_230400 ((0<<9)|(0<<6)) -#define SOLO_BAUDRATE_115200 ((0<<9)|(1<<6)) -#define SOLO_BAUDRATE_57600 ((0<<9)|(2<<6)) -#define SOLO_BAUDRATE_38400 ((0<<9)|(3<<6)) -#define SOLO_BAUDRATE_19200 ((0<<9)|(4<<6)) -#define SOLO_BAUDRATE_9600 ((0<<9)|(5<<6)) -#define SOLO_BAUDRATE_4800 ((0<<9)|(6<<6)) -#define SOLO_BAUDRATE_2400 ((1<<9)|(6<<6)) -#define SOLO_BAUDRATE_1200 ((2<<9)|(6<<6)) -#define SOLO_BAUDRATE_300 ((3<<9)|(6<<6)) - -#define SOLO_UART_DATA_BIT_8 (3<<4) -#define SOLO_UART_DATA_BIT_7 (2<<4) -#define SOLO_UART_DATA_BIT_6 (1<<4) -#define SOLO_UART_DATA_BIT_5 (0<<4) - -#define SOLO_UART_STOP_BIT_1 (0<<2) -#define SOLO_UART_STOP_BIT_2 (1<<2) - -#define SOLO_UART_PARITY_NONE (0<<0) -#define SOLO_UART_PARITY_EVEN (2<<0) -#define SOLO_UART_PARITY_ODD (3<<0) - -#define SOLO_UART_STATUS(n) (0x0BA4 + ((n)*0x20)) -#define SOLO_UART_CTS (1<<15) -#define SOLO_UART_RX_BUSY (1<<14) -#define SOLO_UART_OVERRUN (1<<13) -#define SOLO_UART_FRAME_ERR (1<<12) -#define SOLO_UART_PARITY_ERR (1<<11) -#define SOLO_UART_TX_BUSY (1<<5) - -#define SOLO_UART_RX_BUFF_CNT(stat) (((stat)>>6) & 0x1f) -#define SOLO_UART_RX_BUFF_SIZE 8 -#define SOLO_UART_TX_BUFF_CNT(stat) (((stat)>>0) & 0x1f) -#define SOLO_UART_TX_BUFF_SIZE 8 - -#define SOLO_UART_TX_DATA(n) (0x0BA8 + ((n)*0x20)) -#define SOLO_UART_TX_DATA_PUSH (1<<8) -#define SOLO_UART_RX_DATA(n) (0x0BAC + ((n)*0x20)) -#define SOLO_UART_RX_DATA_POP (1<<8) - -#define SOLO_TIMER_CLOCK_NUM 0x0be0 -#define SOLO_TIMER_WATCHDOG 0x0be4 -#define SOLO_TIMER_USEC 0x0be8 -#define SOLO_TIMER_SEC 0x0bec - -#define SOLO_AUDIO_CONTROL 0x0D00 -#define SOLO_AUDIO_ENABLE (1<<31) -#define SOLO_AUDIO_MASTER_MODE (1<<30) -#define SOLO_AUDIO_I2S_MODE (1<<29) -#define SOLO_AUDIO_I2S_LR_SWAP (1<<27) -#define SOLO_AUDIO_I2S_8BIT (1<<26) -#define SOLO_AUDIO_I2S_MULTI(n) ((n)<<24) -#define SOLO_AUDIO_MIX_9TO0 (1<<23) -#define SOLO_AUDIO_DEC_9TO0_VOL(n) ((n)<<20) -#define SOLO_AUDIO_MIX_19TO10 (1<<19) -#define SOLO_AUDIO_DEC_19TO10_VOL(n) ((n)<<16) -#define SOLO_AUDIO_MODE(n) ((n)<<0) -#define SOLO_AUDIO_SAMPLE 0x0D04 -#define SOLO_AUDIO_EE_MODE_ON (1<<30) -#define SOLO_AUDIO_EE_ENC_CH(ch) ((ch)<<25) -#define SOLO_AUDIO_BITRATE(n) ((n)<<16) -#define SOLO_AUDIO_CLK_DIV(n) ((n)<<0) -#define SOLO_AUDIO_FDMA_INTR 0x0D08 -#define SOLO_AUDIO_FDMA_INTERVAL(n) ((n)<<19) -#define SOLO_AUDIO_INTR_ORDER(n) ((n)<<16) -#define SOLO_AUDIO_FDMA_BASE(n) ((n)<<0) -#define SOLO_AUDIO_EVOL_0 0x0D0C -#define SOLO_AUDIO_EVOL_1 0x0D10 -#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10)) -#define SOLO_AUDIO_STA 0x0D14 - - -#define SOLO_WATCHDOG 0x0BE4 -#define WATCHDOG_STAT(status) (status<<8) -#define WATCHDOG_TIME(sec) (sec&0xff) - -#endif /* __SOLO6010_REGISTERS_H */ diff --git a/drivers/staging/solo6x10/solo6010-tw28.c b/drivers/staging/solo6x10/solo6010-tw28.c deleted file mode 100644 index 905a6ad..0000000 --- a/drivers/staging/solo6x10/solo6010-tw28.c +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include - -#include "solo6010.h" -#include "solo6010-tw28.h" - -/* XXX: Some of these values are masked into an 8-bit regs, and shifted - * around for other 8-bit regs. What are the magic bits in these values? */ -#define DEFAULT_HDELAY_NTSC (32 - 4) -#define DEFAULT_HACTIVE_NTSC (720 + 16) -#define DEFAULT_VDELAY_NTSC (7 - 2) -#define DEFAULT_VACTIVE_NTSC (240 + 4) - -#define DEFAULT_HDELAY_PAL (32 + 4) -#define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL) -#define DEFAULT_VDELAY_PAL (6) -#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) - -static u8 tbl_tw2864_template[] = { - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, - 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01, - 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, - 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, - 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, -}; - -static u8 tbl_tw2865_ntsc_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */ - 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, - 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ - 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, - 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, - 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, - 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ - 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8, - 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, - 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, -}; - -static u8 tbl_tw2865_pal_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */ - 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, - 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ - 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, - 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, - 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, - 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ - 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8, - 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, - 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, -}; - -#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) - -static u8 tw_readbyte(struct solo6010_dev *solo_dev, int chip_id, u8 tw6x_off, - u8 tw_off) -{ - if (is_tw286x(solo_dev, chip_id)) - return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw6x_off); - else - return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw_off); -} - -static void tw_writebyte(struct solo6010_dev *solo_dev, int chip_id, - u8 tw6x_off, u8 tw_off, u8 val) -{ - if (is_tw286x(solo_dev, chip_id)) - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw6x_off, val); - else - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw_off, val); -} - -static void tw_write_and_verify(struct solo6010_dev *solo_dev, u8 addr, u8 off, - u8 val) -{ - int i; - - for (i = 0; i < 5; i++) { - u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); - if (rval == val) - return; - - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val); - msleep_interruptible(1); - } - -/* printk("solo6010/tw28: Error writing register: %02x->%02x [%02x]\n", - addr, off, val); */ -} - -static int tw2865_setup(struct solo6010_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_tw2865_common[256]; - int i; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) - memcpy(tbl_tw2865_common, tbl_tw2865_pal_template, - sizeof(tbl_tw2865_common)); - else - memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template, - sizeof(tbl_tw2865_common)); - - /* ALINK Mode */ - if (solo_dev->nr_chans == 4) { - tbl_tw2865_common[0xd2] = 0x01; - tbl_tw2865_common[0xcf] = 0x00; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2865_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2865_common[0xcf] = 0x80; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2865_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2865_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2865_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2865_common[0xcf] = 0x80; - } - - for (i = 0; i < 0xff; i++) { - /* Skip read only registers */ - if (i >= 0xb8 && i <= 0xc1) - continue; - if ((i & ~0x30) == 0x00 || - (i & ~0x30) == 0x0c || - (i & ~0x30) == 0x0d) - continue; - if (i >= 0xc4 && i <= 0xc7) - continue; - if (i == 0xfd) - continue; - - tw_write_and_verify(solo_dev, dev_addr, i, - tbl_tw2865_common[i]); - } - - return 0; -} - -static int tw2864_setup(struct solo6010_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)]; - int i; - - memcpy(tbl_tw2864_common, tbl_tw2864_template, - sizeof(tbl_tw2864_common)); - - if (solo_dev->tw2865 == 0) { - /* IRQ Mode */ - if (solo_dev->nr_chans == 4) { - tbl_tw2864_common[0xd2] = 0x01; - tbl_tw2864_common[0xcf] = 0x00; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2864_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x40; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2864_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2864_common[0xcf] = 0x40; - } - } else { - /* ALINK Mode. Assumes that the first tw28xx is a - * 2865 and these are in cascade. */ - for (i = 0; i <= 4; i++) - tbl_tw2864_common[0x08 | i << 4] = 0x12; - - if (solo_dev->nr_chans == 8) { - tbl_tw2864_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x80; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2864_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2864_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2864_common[0xcf] = 0x80; - } - } - - /* NTSC or PAL */ - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) { - for (i = 0; i < 4; i++) { - tbl_tw2864_common[0x07 | (i << 4)] |= 0x10; - tbl_tw2864_common[0x08 | (i << 4)] |= 0x06; - tbl_tw2864_common[0x0a | (i << 4)] |= 0x08; - tbl_tw2864_common[0x0b | (i << 4)] |= 0x13; - tbl_tw2864_common[0x0e | (i << 4)] |= 0x01; - } - tbl_tw2864_common[0x9d] = 0x90; - tbl_tw2864_common[0xf3] = 0x00; - tbl_tw2864_common[0xf4] = 0xa0; - } - - for (i = 0; i < 0xff; i++) { - /* Skip read only registers */ - if (i >= 0xb8 && i <= 0xc1) - continue; - if ((i & ~0x30) == 0x00 || - (i & ~0x30) == 0x0c || - (i & ~0x30) == 0x0d) - continue; - if (i == 0x74 || i == 0x77 || i == 0x78 || - i == 0x79 || i == 0x7a) - continue; - if (i == 0xfd) - continue; - - tw_write_and_verify(solo_dev, dev_addr, i, - tbl_tw2864_common[i]); - } - - return 0; -} - -static int tw2815_setup(struct solo6010_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_ntsc_tw2815_common[] = { - 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80, - 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11, - }; - - u8 tbl_pal_tw2815_common[] = { - 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80, - 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11, - }; - - u8 tbl_tw2815_sfr[] = { - 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */ - 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */ - 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00, - 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */ - 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88, - 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */ - }; - u8 *tbl_tw2815_common; - int i; - int ch; - - tbl_ntsc_tw2815_common[0x06] = 0; - - /* Horizontal Delay Control */ - tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8); - - /* Horizontal Active Control */ - tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2); - - /* Vertical Delay Control */ - tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4); - - /* Vertical Active Control */ - tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5); - - tbl_pal_tw2815_common[0x06] = 0; - - /* Horizontal Delay Control */ - tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8); - - /* Horizontal Active Control */ - tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2); - - /* Vertical Delay Control */ - tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4); - - /* Vertical Active Control */ - tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5); - - tbl_tw2815_common = - (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ? - tbl_ntsc_tw2815_common : tbl_pal_tw2815_common; - - /* Dual ITU-R BT.656 format */ - tbl_tw2815_common[0x0d] |= 0x04; - - /* Audio configuration */ - tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6); - - if (solo_dev->nr_chans == 4) { - tbl_tw2815_sfr[0x63 - 0x40] |= 1; - tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2815_sfr[0x63 - 0x40] |= 2; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2815_sfr[0x63 - 0x40] |= 3; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; - } - - /* Output mode of R_ADATM pin (0 mixing, 1 record) */ - /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */ - - /* 8KHz, used to be 16KHz, but changed for remote client compat */ - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2; - tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2; - - /* Playback of right channel */ - tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5; - - /* Reserved value (XXX ??) */ - tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5; - - /* Analog output gain and mix ratio playback on full */ - tbl_tw2815_sfr[0x70 - 0x40] |= 0xff; - /* Select playback audio and mute all except */ - tbl_tw2815_sfr[0x71 - 0x40] |= 0x10; - tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f; - - /* End of audio configuration */ - - for (ch = 0; ch < 4; ch++) { - tbl_tw2815_common[0x0d] &= ~3; - switch (ch) { - case 0: - tbl_tw2815_common[0x0d] |= 0x21; - break; - case 1: - tbl_tw2815_common[0x0d] |= 0x20; - break; - case 2: - tbl_tw2815_common[0x0d] |= 0x23; - break; - case 3: - tbl_tw2815_common[0x0d] |= 0x22; - break; - } - - for (i = 0; i < 0x0f; i++) { - if (i == 0x00) - continue; /* read-only */ - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - dev_addr, (ch * 0x10) + i, - tbl_tw2815_common[i]); - } - } - - for (i = 0x40; i < 0x76; i++) { - /* Skip read-only and nop registers */ - if (i == 0x40 || i == 0x59 || i == 0x5a || - i == 0x5d || i == 0x5e || i == 0x5f) - continue; - - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i, - tbl_tw2815_sfr[i - 0x40]); - } - - return 0; -} - -#define FIRST_ACTIVE_LINE 0x0008 -#define LAST_ACTIVE_LINE 0x0102 - -static void saa7128_setup(struct solo6010_dev *solo_dev) -{ - int i; - unsigned char regs[128] = { - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, - 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00, - 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, - 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, - 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, - 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, - 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e, - 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77, - 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00, - 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff, - }; - - regs[0x7A] = FIRST_ACTIVE_LINE & 0xff; - regs[0x7B] = LAST_ACTIVE_LINE & 0xff; - regs[0x7C] = ((1 << 7) | - (((LAST_ACTIVE_LINE >> 8) & 1) << 6) | - (((FIRST_ACTIVE_LINE >> 8) & 1) << 4)); - - /* PAL: XXX: We could do a second set of regs to avoid this */ - if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) { - regs[0x28] = 0xE1; - - regs[0x5A] = 0x0F; - regs[0x61] = 0x02; - regs[0x62] = 0x35; - regs[0x63] = 0xCB; - regs[0x64] = 0x8A; - regs[0x65] = 0x09; - regs[0x66] = 0x2A; - - regs[0x6C] = 0xf1; - regs[0x6E] = 0x20; - - regs[0x7A] = 0x06 + 12; - regs[0x7b] = 0x24 + 12; - regs[0x7c] |= 1 << 6; - } - - /* First 0x25 bytes are read-only? */ - for (i = 0x26; i < 128; i++) { - if (i == 0x60 || i == 0x7D) - continue; - solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]); - } - - return; -} - -int solo_tw28_init(struct solo6010_dev *solo_dev) -{ - int i; - u8 value; - - /* Detect techwell chip type */ - for (i = 0; i < TW_NUM_CHIP; i++) { - value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), 0xFF); - - switch (value >> 3) { - case 0x18: - solo_dev->tw2865 |= 1 << i; - solo_dev->tw28_cnt++; - break; - case 0x0c: - solo_dev->tw2864 |= 1 << i; - solo_dev->tw28_cnt++; - break; - default: - value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), 0x59); - if ((value >> 3) == 0x04) { - solo_dev->tw2815 |= 1 << i; - solo_dev->tw28_cnt++; - } - } - } - - if (!solo_dev->tw28_cnt) - return -EINVAL; - - saa7128_setup(solo_dev); - - for (i = 0; i < solo_dev->tw28_cnt; i++) { - if ((solo_dev->tw2865 & (1 << i))) - tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - else if ((solo_dev->tw2864 & (1 << i))) - tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - else - tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - } - - dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:", - solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s"); - - if (solo_dev->tw2865) - printk(" tw2865[%d]", hweight32(solo_dev->tw2865)); - if (solo_dev->tw2864) - printk(" tw2864[%d]", hweight32(solo_dev->tw2864)); - if (solo_dev->tw2815) - printk(" tw2815[%d]", hweight32(solo_dev->tw2815)); - printk("\n"); - - return 0; -} - -/* - * We accessed the video status signal in the Techwell chip through - * iic/i2c because the video status reported by register REG_VI_STATUS1 - * (address 0x012C) of the SOLO6010 chip doesn't give the correct video - * status signal values. - */ -int tw28_get_video_status(struct solo6010_dev *solo_dev, u8 ch) -{ - u8 val, chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR, - TW_AV_STAT_ADDR) & 0x0f; - - return val & (1 << ch) ? 1 : 0; -} - -#if 0 -/* Status of audio from up to 4 techwell chips are combined into 1 variable. - * See techwell datasheet for details. */ -u16 tw28_get_audio_status(struct solo6010_dev *solo_dev) -{ - u8 val; - u16 status = 0; - int i; - - for (i = 0; i < solo_dev->tw28_cnt; i++) { - val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR, - TW_AV_STAT_ADDR) & 0xf0) >> 4; - status |= val << (i * 4); - } - - return status; -} -#endif - -int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch, - s32 val) -{ - char sval; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - if (val > 255 || val < 0) - return -ERANGE; - - switch (ctrl) { - case V4L2_CID_SHARPNESS: - /* Only 286x has sharpness */ - if (val > 0x0f || val < 0) - return -ERANGE; - if (is_tw286x(solo_dev, chip_num)) { - u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num)); - v &= 0xf0; - v |= val; - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num), v); - } else if (val != 0) - return -ERANGE; - break; - - case V4L2_CID_HUE: - if (is_tw286x(solo_dev, chip_num)) - sval = val - 128; - else - sval = (char)val; - tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), - TW_HUE_ADDR(ch), sval); - - break; - - case V4L2_CID_SATURATION: - if (is_tw286x(solo_dev, chip_num)) { - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SATURATIONU_ADDR(ch), val); - } - tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch), - TW_SATURATION_ADDR(ch), val); - - break; - - case V4L2_CID_CONTRAST: - tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch), - TW_CONTRAST_ADDR(ch), val); - break; - - case V4L2_CID_BRIGHTNESS: - if (is_tw286x(solo_dev, chip_num)) - sval = val - 128; - else - sval = (char)val; - tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch), - TW_BRIGHTNESS_ADDR(ch), sval); - - break; - default: - return -EINVAL; - } - - return 0; -} - -int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch, - s32 *val) -{ - u8 rval, chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - switch (ctrl) { - case V4L2_CID_SHARPNESS: - /* Only 286x has sharpness */ - if (is_tw286x(solo_dev, chip_num)) { - rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num)); - *val = rval & 0x0f; - } else - *val = 0; - break; - case V4L2_CID_HUE: - rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), - TW_HUE_ADDR(ch)); - if (is_tw286x(solo_dev, chip_num)) - *val = (s32)((char)rval) + 128; - else - *val = rval; - break; - case V4L2_CID_SATURATION: - *val = tw_readbyte(solo_dev, chip_num, - TW286x_SATURATIONU_ADDR(ch), - TW_SATURATION_ADDR(ch)); - break; - case V4L2_CID_CONTRAST: - *val = tw_readbyte(solo_dev, chip_num, - TW286x_CONTRAST_ADDR(ch), - TW_CONTRAST_ADDR(ch)); - break; - case V4L2_CID_BRIGHTNESS: - rval = tw_readbyte(solo_dev, chip_num, - TW286x_BRIGHTNESS_ADDR(ch), - TW_BRIGHTNESS_ADDR(ch)); - if (is_tw286x(solo_dev, chip_num)) - *val = (s32)((char)rval) + 128; - else - *val = rval; - break; - default: - return -EINVAL; - } - - return 0; -} - -#if 0 -/* - * For audio output volume, the output channel is only 1. In this case we - * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used - * is the base address of the techwell chip. - */ -void tw2815_Set_AudioOutVol(struct solo6010_dev *solo_dev, unsigned int u_val) -{ - unsigned int val; - unsigned int chip_num; - - chip_num = (solo_dev->nr_chans - 1) / 4; - - val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, - TW_AUDIO_OUTPUT_VOL_ADDR); - - u_val = (val & 0x0f) | (u_val << 4); - - tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, - TW_AUDIO_OUTPUT_VOL_ADDR, u_val); -} -#endif - -u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch) -{ - u8 val; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - val = tw_readbyte(solo_dev, chip_num, - TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch)); - - return (ch % 2) ? (val >> 4) : (val & 0x0f); -} - -void tw28_set_audio_gain(struct solo6010_dev *solo_dev, u8 ch, u8 val) -{ - u8 old_val; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - old_val = tw_readbyte(solo_dev, chip_num, - TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch)); - - val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) | - ((ch % 2) ? (val << 4) : val); - - tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch), val); -} diff --git a/drivers/staging/solo6x10/solo6010-tw28.h b/drivers/staging/solo6x10/solo6010-tw28.h deleted file mode 100644 index a7eecfa..0000000 --- a/drivers/staging/solo6x10/solo6010-tw28.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __SOLO6010_TW28_H -#define __SOLO6010_TW28_H - -#include "solo6010.h" - -#define TW_NUM_CHIP 4 -#define TW_BASE_ADDR 0x28 -#define TW_CHIP_OFFSET_ADDR(n) (TW_BASE_ADDR + (n)) - -/* tw2815 */ -#define TW_AV_STAT_ADDR 0x5a -#define TW_HUE_ADDR(n) (0x07 | ((n) << 4)) -#define TW_SATURATION_ADDR(n) (0x08 | ((n) << 4)) -#define TW_CONTRAST_ADDR(n) (0x09 | ((n) << 4)) -#define TW_BRIGHTNESS_ADDR(n) (0x0a | ((n) << 4)) -#define TW_AUDIO_OUTPUT_VOL_ADDR 0x70 -#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0)) - -/* tw286x */ -#define TW286X_AV_STAT_ADDR 0xfd -#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4)) -#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4)) -#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4)) -#define TW286x_CONTRAST_ADDR(n) (0x02 | ((n) << 4)) -#define TW286x_BRIGHTNESS_ADDR(n) (0x01 | ((n) << 4)) -#define TW286x_SHARPNESS(n) (0x03 | ((n) << 4)) -#define TW286x_AUDIO_OUTPUT_VOL_ADDR 0xdf -#define TW286x_AUDIO_INPUT_GAIN_ADDR(n) (0xD0 + ((n > 1) ? 1 : 0)) - -int solo_tw28_init(struct solo6010_dev *solo_dev); - -int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch, - s32 val); -int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch, - s32 *val); - -u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch); -void tw28_set_audio_gain(struct solo6010_dev *solo_dev, u8 ch, u8 val); -int tw28_get_video_status(struct solo6010_dev *solo_dev, u8 ch); - -#if 0 -unsigned int tw2815_get_audio_status(struct SOLO6010 *solo6010); -void tw2815_Set_AudioOutVol(struct SOLO6010 *solo6010, unsigned int u_val); -#endif - -#endif /* __SOLO6010_TW28_H */ diff --git a/drivers/staging/solo6x10/solo6010-v4l2-enc.c b/drivers/staging/solo6x10/solo6010-v4l2-enc.c deleted file mode 100644 index 6fbd50e..0000000 --- a/drivers/staging/solo6x10/solo6010-v4l2-enc.c +++ /dev/null @@ -1,1827 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "solo6010.h" -#include "solo6010-tw28.h" -#include "solo6010-jpeg.h" - -#define MIN_VID_BUFFERS 4 -#define FRAME_BUF_SIZE (128 * 1024) -#define MP4_QS 16 - -static int solo_enc_thread(void *data); - -extern unsigned video_nr; - -struct solo_enc_fh { - struct solo_enc_dev *enc; - u32 fmt; - u16 rd_idx; - u8 enc_on; - enum solo_enc_types type; - struct videobuf_queue vidq; - struct list_head vidq_active; - struct task_struct *kthread; - struct p2m_desc desc[SOLO_NR_P2M_DESC]; -}; - -static const u32 solo_user_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_SHARPNESS, - 0 -}; - -static const u32 solo_mpeg_ctrls[] = { - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 0 -}; - -static const u32 solo_private_ctrls[] = { - V4L2_CID_MOTION_ENABLE, - V4L2_CID_MOTION_THRESHOLD, - 0 -}; - -static const u32 solo_fmtx_ctrls[] = { - V4L2_CID_RDS_TX_RADIO_TEXT, - 0 -}; - -static const u32 *solo_ctrl_classes[] = { - solo_user_ctrls, - solo_mpeg_ctrls, - solo_fmtx_ctrls, - solo_private_ctrls, - NULL -}; - -static int solo_is_motion_on(struct solo_enc_dev *solo_enc) -{ - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - u8 ch = solo_enc->ch; - - if (solo_dev->motion_mask & (1 << ch)) - return 1; - return 0; -} - -static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) -{ - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - u8 ch = solo_enc->ch; - - spin_lock(&solo_enc->lock); - - if (on) - solo_dev->motion_mask |= (1 << ch); - else - solo_dev->motion_mask &= ~(1 << ch); - - /* Do this regardless of if we are turning on or off */ - solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, - 1 << solo_enc->ch); - solo_enc->motion_detected = 0; - - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, - SOLO_VI_MOTION_EN(solo_dev->motion_mask) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - - if (solo_dev->motion_mask) - solo6010_irq_on(solo_dev, SOLO_IRQ_MOTION); - else - solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION); - - spin_unlock(&solo_enc->lock); -} - -/* Should be called with solo_enc->lock held */ -static void solo_update_mode(struct solo_enc_dev *solo_enc) -{ - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - - assert_spin_locked(&solo_enc->lock); - - solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; - solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); - - switch (solo_enc->mode) { - case SOLO_ENC_MODE_CIF: - solo_enc->width = solo_dev->video_hsize >> 1; - solo_enc->height = solo_dev->video_vsize; - break; - case SOLO_ENC_MODE_D1: - solo_enc->width = solo_dev->video_hsize; - solo_enc->height = solo_dev->video_vsize << 1; - solo_enc->bw_weight <<= 2; - break; - default: - WARN(1, "mode is unknown\n"); - } -} - -/* Should be called with solo_enc->lock held */ -static int solo_enc_on(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - u8 ch = solo_enc->ch; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - u8 interval; - - assert_spin_locked(&solo_enc->lock); - - if (fh->enc_on) - return 0; - - solo_update_mode(solo_enc); - - /* Make sure to bw check on first reader */ - if (!atomic_read(&solo_enc->readers)) { - if (solo_enc->bw_weight > solo_dev->enc_bw_remain) - return -EBUSY; - else - solo_dev->enc_bw_remain -= solo_enc->bw_weight; - } - - fh->enc_on = 1; - fh->rd_idx = solo_enc->solo_dev->enc_wr_idx; - - if (fh->type == SOLO_ENC_TYPE_EXT) - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); - - if (atomic_inc_return(&solo_enc->readers) > 1) - return 0; - - /* Disable all encoding for this channel */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); - - /* Common for both std and ext encoding */ - solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), - solo_enc->interlaced ? 1 : 0); - - if (solo_enc->interlaced) - interval = solo_enc->interval - 1; - else - interval = solo_enc->interval; - - /* Standard encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); - - /* Extended encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); - - /* Enables the standard encoder */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); - - /* Settle down Beavis... */ - mdelay(10); - - return 0; -} - -static void solo_enc_off(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - - if (!fh->enc_on) - return; - - if (fh->kthread) { - kthread_stop(fh->kthread); - fh->kthread = NULL; - } - - solo_dev->enc_bw_remain += solo_enc->bw_weight; - fh->enc_on = 0; - - if (atomic_dec_return(&solo_enc->readers) > 0) - return; - - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); -} - -static int solo_start_fh_thread(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - - fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6010_NAME "_enc"); - - /* Oops, we had a problem */ - if (IS_ERR(fh->kthread)) { - spin_lock(&solo_enc->lock); - solo_enc_off(fh); - spin_unlock(&solo_enc->lock); - - return PTR_ERR(fh->kthread); - } - - return 0; -} - -static void enc_reset_gop(struct solo6010_dev *solo_dev, u8 ch) -{ - BUG_ON(ch >= solo_dev->nr_chans); - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), 1); - solo_dev->v4l2_enc[ch]->reset_gop = 1; -} - -static int enc_gop_reset(struct solo6010_dev *solo_dev, u8 ch, u8 vop) -{ - BUG_ON(ch >= solo_dev->nr_chans); - if (!solo_dev->v4l2_enc[ch]->reset_gop) - return 0; - if (vop) - return 1; - solo_dev->v4l2_enc[ch]->reset_gop = 0; - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), - solo_dev->v4l2_enc[ch]->gop); - return 0; -} - -static void enc_write_sg(struct scatterlist *sglist, void *buf, int size) -{ - struct scatterlist *sg; - u8 *src = buf; - - for (sg = sglist; sg && size > 0; sg = sg_next(sg)) { - u8 *p = sg_virt(sg); - size_t len = sg_dma_len(sg); - int i; - - for (i = 0; i < len && size; i++) - p[i] = *(src++); - } -} - -static int enc_get_mpeg_dma_sg(struct solo6010_dev *solo_dev, - struct p2m_desc *desc, - struct scatterlist *sglist, int skip, - unsigned int off, unsigned int size) -{ - int ret; - - if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, - desc, 0, sglist, skip, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, - sglist, skip, SOLO_MP4E_EXT_ADDR(solo_dev) + off, - SOLO_MP4E_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, - sglist, skip + SOLO_MP4E_EXT_SIZE(solo_dev) - off, - SOLO_MP4E_EXT_ADDR(solo_dev), - size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); - - return ret; -} - -static int enc_get_mpeg_dma_t(struct solo6010_dev *solo_dev, - dma_addr_t buf, unsigned int off, - unsigned int size) -{ - int ret; - - if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, - SOLO_MP4E_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, - buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off, - SOLO_MP4E_EXT_ADDR(solo_dev), - size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); - - return ret; -} - -static int enc_get_mpeg_dma(struct solo6010_dev *solo_dev, void *buf, - unsigned int off, unsigned int size) -{ - int ret; - - dma_addr_t dma_addr = pci_map_single(solo_dev->pdev, buf, size, - PCI_DMA_FROMDEVICE); - ret = enc_get_mpeg_dma_t(solo_dev, dma_addr, off, size); - pci_unmap_single(solo_dev->pdev, dma_addr, size, PCI_DMA_FROMDEVICE); - - return ret; -} - -static int enc_get_jpeg_dma_sg(struct solo6010_dev *solo_dev, - struct p2m_desc *desc, - struct scatterlist *sglist, int skip, - unsigned int off, unsigned int size) -{ - int ret; - - if (off > SOLO_JPEG_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, - desc, 0, sglist, skip, - SOLO_JPEG_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, - sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off, - SOLO_JPEG_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, - sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off, - SOLO_JPEG_EXT_ADDR(solo_dev), - size + off - SOLO_JPEG_EXT_SIZE(solo_dev)); - - return ret; -} - -/* Returns true of __chk is within the first __range bytes of __off */ -#define OFF_IN_RANGE(__off, __range, __chk) \ - ((__off <= __chk) && ((__off + __range) >= __chk)) - -static void solo_jpeg_header(struct solo_enc_dev *solo_enc, - struct videobuf_dmabuf *vbuf) -{ - struct scatterlist *sg; - void *src = jpeg_header; - size_t copied = 0; - size_t to_copy = sizeof(jpeg_header); - - for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) { - size_t this_copy = min(sg_dma_len(sg), - (unsigned int)(to_copy - copied)); - u8 *p = sg_virt(sg); - - memcpy(p, src + copied, this_copy); - - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5)) - p[(SOF0_START + 5) - copied] = - 0xff & (solo_enc->height >> 8); - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6)) - p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height; - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7)) - p[(SOF0_START + 7) - copied] = - 0xff & (solo_enc->width >> 8); - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8)) - p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width; - - copied += this_copy; - } -} - -static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, - struct videobuf_buffer *vb, - struct videobuf_dmabuf *vbuf) -{ - struct solo6010_dev *solo_dev = fh->enc->solo_dev; - int size = enc_buf->jpeg_size; - - /* Copy the header first (direct write) */ - solo_jpeg_header(fh->enc, vbuf); - - vb->size = size + sizeof(jpeg_header); - - /* Grab the jpeg frame */ - return enc_get_jpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, - sizeof(jpeg_header), - enc_buf->jpeg_off, size); -} - -static inline int vop_interlaced(__le32 *vh) -{ - return (__le32_to_cpu(vh[0]) >> 30) & 1; -} - -static inline u32 vop_size(__le32 *vh) -{ - return __le32_to_cpu(vh[0]) & 0xFFFFF; -} - -static inline u8 vop_hsize(__le32 *vh) -{ - return (__le32_to_cpu(vh[1]) >> 8) & 0xFF; -} - -static inline u8 vop_vsize(__le32 *vh) -{ - return __le32_to_cpu(vh[1]) & 0xFF; -} - -/* must be called with *bits % 8 = 0 */ -static void write_bytes(u8 **out, unsigned *bits, const u8 *src, unsigned count) -{ - memcpy(*out, src, count); - *out += count; - *bits += count * 8; -} - -static void write_bits(u8 **out, unsigned *bits, u32 value, unsigned count) -{ - - value <<= 32 - count; // shift to the right - - while (count--) { - **out <<= 1; - **out |= !!(value & (1 << 31)); /* MSB */ - value <<= 1; - if (++(*bits) % 8 == 0) - (*out)++; - } -} - -static void write_ue(u8 **out, unsigned *bits, unsigned value) /* H.264 only */ -{ - uint32_t max = 0, cnt = 0; - - while (value > max) { - max = (max + 2) * 2 - 2; - cnt++; - } - write_bits(out, bits, 1, cnt + 1); - write_bits(out, bits, ~(max - value), cnt); -} - -static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */ -{ - if (value <= 0) - write_ue(out, bits, -value * 2); - else - write_ue(out, bits, value * 2 - 1); -} - -static void write_mpeg4_end(u8 **out, unsigned *bits) -{ - write_bits(out, bits, 0, 1); - /* align on 32-bit boundary */ - if (*bits % 32) - write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32); -} - -static void write_h264_end(u8 **out, unsigned *bits, int align) -{ - write_bits(out, bits, 1, 1); - while ((*bits) % 8) - write_bits(out, bits, 0, 1); - if (align) - while ((*bits) % 32) - write_bits(out, bits, 0, 1); -} - -static void mpeg4_write_vol(u8 **out, struct solo6010_dev *solo_dev, - __le32 *vh, unsigned fps, unsigned interval) -{ - static const u8 hdr[] = { - 0, 0, 1, 0x00 /* video_object_start_code */, - 0, 0, 1, 0x20 /* video_object_layer_start_code */ - }; - unsigned bits = 0; - unsigned width = vop_hsize(vh) << 4; - unsigned height = vop_vsize(vh) << 4; - unsigned interlaced = vop_interlaced(vh); - - write_bytes(out, &bits, hdr, sizeof(hdr)); - write_bits(out, &bits, 0, 1); /* random_accessible_vol */ - write_bits(out, &bits, 0x04, 8); /* video_object_type_indication: main */ - write_bits(out, &bits, 1, 1); /* is_object_layer_identifier */ - write_bits(out, &bits, 2, 4); /* video_object_layer_verid: table V2-39 */ - write_bits(out, &bits, 0, 3); /* video_object_layer_priority */ - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - write_bits(out, &bits, 3, 4); /* aspect_ratio_info, assuming 4:3 */ - else - write_bits(out, &bits, 2, 4); - write_bits(out, &bits, 1, 1); /* vol_control_parameters */ - write_bits(out, &bits, 1, 2); /* chroma_format: 4:2:0 */ - write_bits(out, &bits, 1, 1); /* low_delay */ - write_bits(out, &bits, 0, 1); /* vbv_parameters */ - write_bits(out, &bits, 0, 2); /* video_object_layer_shape: rectangular */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, fps, 16); /* vop_time_increment_resolution */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, 1, 1); /* fixed_vop_rate */ - write_bits(out, &bits, interval, 15); /* fixed_vop_time_increment */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, width, 13); /* video_object_layer_width */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, height, 13); /* video_object_layer_height */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, interlaced, 1); /* interlaced */ - write_bits(out, &bits, 1, 1); /* obmc_disable */ - write_bits(out, &bits, 0, 2); /* sprite_enable */ - write_bits(out, &bits, 0, 1); /* not_8_bit */ - write_bits(out, &bits, 1, 0); /* quant_type */ - write_bits(out, &bits, 0, 1); /* load_intra_quant_mat */ - write_bits(out, &bits, 0, 1); /* load_nonintra_quant_mat */ - write_bits(out, &bits, 0, 1); /* quarter_sample */ - write_bits(out, &bits, 1, 1); /* complexity_estimation_disable */ - write_bits(out, &bits, 1, 1); /* resync_marker_disable */ - write_bits(out, &bits, 0, 1); /* data_partitioned */ - write_bits(out, &bits, 0, 1); /* newpred_enable */ - write_bits(out, &bits, 0, 1); /* reduced_resolution_vop_enable */ - write_bits(out, &bits, 0, 1); /* scalability */ - write_mpeg4_end(out, &bits); -} - -static void h264_write_vol(u8 **out, struct solo6010_dev *solo_dev, __le32 *vh) -{ - static const u8 sps[] = { - 0, 0, 0, 1 /* start code */, 0x67, 66 /* profile_idc */, - 0 /* constraints */, 30 /* level_idc */ - }; - static const u8 pps[] = { - 0, 0, 0, 1 /* start code */, 0x68 - }; - - unsigned bits = 0; - unsigned mbs_w = vop_hsize(vh); - unsigned mbs_h = vop_vsize(vh); - - write_bytes(out, &bits, sps, sizeof(sps)); - write_ue(out, &bits, 0); /* seq_parameter_set_id */ - write_ue(out, &bits, 5); /* log2_max_frame_num_minus4 */ - write_ue(out, &bits, 0); /* pic_order_cnt_type */ - write_ue(out, &bits, 6); /* log2_max_pic_order_cnt_lsb_minus4 */ - write_ue(out, &bits, 1); /* max_num_ref_frames */ - write_bits(out, &bits, 0, 1); /* gaps_in_frame_num_value_allowed_flag */ - write_ue(out, &bits, mbs_w - 1); /* pic_width_in_mbs_minus1 */ - write_ue(out, &bits, mbs_h - 1); /* pic_height_in_map_units_minus1 */ - write_bits(out, &bits, 1, 1); /* frame_mbs_only_flag */ - write_bits(out, &bits, 1, 1); /* direct_8x8_frame_field_flag */ - write_bits(out, &bits, 0, 1); /* frame_cropping_flag */ - write_bits(out, &bits, 0, 1); /* vui_parameters_present_flag */ - write_h264_end(out, &bits, 0); - - write_bytes(out, &bits, pps, sizeof(pps)); - write_ue(out, &bits, 0); /* pic_parameter_set_id */ - write_ue(out, &bits, 0); /* seq_parameter_set_id */ - write_bits(out, &bits, 0, 1); /* entropy_coding_mode_flag */ - write_bits(out, &bits, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */ - write_ue(out, &bits, 0); /* num_slice_groups_minus1 */ - write_ue(out, &bits, 0); /* num_ref_idx_l0_default_active_minus1 */ - write_ue(out, &bits, 0); /* num_ref_idx_l1_default_active_minus1 */ - write_bits(out, &bits, 0, 1); /* weighted_pred_flag */ - write_bits(out, &bits, 0, 2); /* weighted_bipred_idc */ - write_se(out, &bits, 0); /* pic_init_qp_minus26 */ - write_se(out, &bits, 0); /* pic_init_qs_minus26 */ - write_se(out, &bits, 2); /* chroma_qp_index_offset */ - write_bits(out, &bits, 0, 1); /* deblocking_filter_control_present_flag */ - write_bits(out, &bits, 1, 1); /* constrained_intra_pred_flag */ - write_bits(out, &bits, 0, 1); /* redundant_pic_cnt_present_flag */ - write_h264_end(out, &bits, 1); -} - -static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, - struct videobuf_buffer *vb, - struct videobuf_dmabuf *vbuf) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - -#define VH_WORDS 16 -#define MAX_VOL_HEADER_LENGTH 64 - - __le32 vh[VH_WORDS]; - int ret; - int frame_size, frame_off; - int skip = 0; - - if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh))) - return -EINVAL; - - /* First get the hardware vop header (not real mpeg) */ - ret = enc_get_mpeg_dma(solo_dev, vh, enc_buf->off, sizeof(vh)); - if (WARN_ON_ONCE(ret)) - return ret; - - if (WARN_ON_ONCE(vop_size(vh) > enc_buf->size)) - return -EINVAL; - - vb->width = vop_hsize(vh) << 4; - vb->height = vop_vsize(vh) << 4; - vb->size = vop_size(vh); - - /* If this is a key frame, add extra m4v header */ - if (!enc_buf->vop) { - u8 header[MAX_VOL_HEADER_LENGTH], *out = header; - - if (solo_dev->flags & FLAGS_6110) - h264_write_vol(&out, solo_dev, vh); - else - mpeg4_write_vol(&out, solo_dev, vh, - solo_dev->fps * 1000, - solo_enc->interval * 1000); - skip = out - header; - enc_write_sg(vbuf->sglist, header, skip); - /* Adjust the dma buffer past this header */ - vb->size += skip; - } - - /* Now get the actual mpeg payload */ - frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); - frame_size = enc_buf->size - sizeof(vh); - - ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, - skip, frame_off, frame_size); - WARN_ON_ONCE(ret); - - return ret; -} - -static void solo_enc_fillbuf(struct solo_enc_fh *fh, - struct videobuf_buffer *vb) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - struct solo_enc_buf *enc_buf = NULL; - struct videobuf_dmabuf *vbuf; - int ret; - int error = 1; - u16 idx = fh->rd_idx; - - while (idx != solo_dev->enc_wr_idx) { - struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx]; - - idx = (idx + 1) % SOLO_NR_RING_BUFS; - - if (ebuf->ch != solo_enc->ch) - continue; - - if (fh->fmt == V4L2_PIX_FMT_MPEG) { - if (fh->type == ebuf->type) { - enc_buf = ebuf; - break; - } - } else { - /* For mjpeg, keep reading to the newest frame */ - enc_buf = ebuf; - } - } - - fh->rd_idx = idx; - - if (WARN_ON_ONCE(!enc_buf)) - goto buf_err; - - if ((fh->fmt == V4L2_PIX_FMT_MPEG && - vb->bsize < enc_buf->size) || - (fh->fmt == V4L2_PIX_FMT_MJPEG && - vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) { - WARN_ON_ONCE(1); - goto buf_err; - } - - vbuf = videobuf_to_dma(vb); - if (WARN_ON_ONCE(!vbuf)) - goto buf_err; - - if (fh->fmt == V4L2_PIX_FMT_MPEG) - ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf); - else - ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf); - - if (!ret) - error = 0; - -buf_err: - if (error) { - vb->state = VIDEOBUF_ERROR; - } else { - vb->field_count++; - vb->ts = enc_buf->ts; - vb->state = VIDEOBUF_DONE; - } - - wake_up(&vb->done); - - return; -} - -static void solo_enc_thread_try(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - struct videobuf_buffer *vb; - - for (;;) { - spin_lock(&solo_enc->lock); - - if (fh->rd_idx == solo_dev->enc_wr_idx) - break; - - if (list_empty(&fh->vidq_active)) - break; - - vb = list_first_entry(&fh->vidq_active, - struct videobuf_buffer, queue); - - if (!waitqueue_active(&vb->done)) - break; - - list_del(&vb->queue); - - spin_unlock(&solo_enc->lock); - - solo_enc_fillbuf(fh, vb); - } - - assert_spin_locked(&solo_enc->lock); - spin_unlock(&solo_enc->lock); -} - -static int solo_enc_thread(void *data) -{ - struct solo_enc_fh *fh = data; - struct solo_enc_dev *solo_enc = fh->enc; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_enc->thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_enc_thread_try(fh); - try_to_freeze(); - } - - remove_wait_queue(&solo_enc->thread_wait, &wait); - - return 0; -} - -void solo_motion_isr(struct solo6010_dev *solo_dev) -{ - u32 status; - int i; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_MOTION); - - status = solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS); - - for (i = 0; i < solo_dev->nr_chans; i++) { - struct solo_enc_dev *solo_enc = solo_dev->v4l2_enc[i]; - - BUG_ON(solo_enc == NULL); - - if (solo_enc->motion_detected) - continue; - if (!(status & (1 << i))) - continue; - - solo_enc->motion_detected = 1; - } -} - -void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev) -{ - struct solo_enc_buf *enc_buf; - u32 mpeg_current, mpeg_next, mpeg_size; - u32 jpeg_current, jpeg_next, jpeg_size; - u32 reg_mpeg_size; - u8 cur_q, vop_type; - u8 ch; - enum solo_enc_types enc_type; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER); - - cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS; - - reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7; - - while (solo_dev->enc_idx != cur_q) { - mpeg_current = solo_reg_read(solo_dev, - SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); - jpeg_current = solo_reg_read(solo_dev, - SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); - solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; - mpeg_next = solo_reg_read(solo_dev, - SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); - jpeg_next = solo_reg_read(solo_dev, - SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); - - ch = (mpeg_current >> 24) & 0x1f; - if (ch >= SOLO_MAX_CHANNELS) { - ch -= SOLO_MAX_CHANNELS; - enc_type = SOLO_ENC_TYPE_EXT; - } else - enc_type = SOLO_ENC_TYPE_STD; - - vop_type = (mpeg_current >> 29) & 3; - - mpeg_current &= 0x00ffffff; - mpeg_next &= 0x00ffffff; - jpeg_current &= 0x00ffffff; - jpeg_next &= 0x00ffffff; - - mpeg_size = (SOLO_MP4E_EXT_SIZE(solo_dev) + - mpeg_next - mpeg_current) % - SOLO_MP4E_EXT_SIZE(solo_dev); - - jpeg_size = (SOLO_JPEG_EXT_SIZE(solo_dev) + - jpeg_next - jpeg_current) % - SOLO_JPEG_EXT_SIZE(solo_dev); - - /* XXX I think this means we had a ring overflow? */ - if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) { - enc_reset_gop(solo_dev, ch); - continue; - } - - /* When resetting the GOP, skip frames until I-frame */ - if (enc_gop_reset(solo_dev, ch, vop_type)) - continue; - - enc_buf = &solo_dev->enc_buf[solo_dev->enc_wr_idx]; - - enc_buf->vop = vop_type; - enc_buf->ch = ch; - enc_buf->off = mpeg_current; - enc_buf->size = mpeg_size; - enc_buf->jpeg_off = jpeg_current; - enc_buf->jpeg_size = jpeg_size; - enc_buf->type = enc_type; - - do_gettimeofday(&enc_buf->ts); - - solo_dev->enc_wr_idx = (solo_dev->enc_wr_idx + 1) % - SOLO_NR_RING_BUFS; - - wake_up_interruptible(&solo_dev->v4l2_enc[ch]->thread_wait); - } - - return; -} - -static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - *size = FRAME_BUF_SIZE; - - if (*count < MIN_VID_BUFFERS) - *count = MIN_VID_BUFFERS; - - return 0; -} - -static int solo_enc_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct solo_enc_fh *fh = vq->priv_data; - struct solo_enc_dev *solo_enc = fh->enc; - - vb->size = FRAME_BUF_SIZE; - if (vb->baddr != 0 && vb->bsize < vb->size) - return -EINVAL; - - /* These properties only change when queue is idle */ - vb->width = solo_enc->width; - vb->height = solo_enc->height; - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int rc = videobuf_iolock(vq, vb, NULL); - if (rc < 0) { - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; - return rc; - } - } - vb->state = VIDEOBUF_PREPARED; - - return 0; -} - -static void solo_enc_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct solo_enc_fh *fh = vq->priv_data; - - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &fh->vidq_active); - wake_up_interruptible(&fh->enc->thread_wait); -} - -static void solo_enc_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops solo_enc_video_qops = { - .buf_setup = solo_enc_buf_setup, - .buf_prepare = solo_enc_buf_prepare, - .buf_queue = solo_enc_buf_queue, - .buf_release = solo_enc_buf_release, -}; - -static unsigned int solo_enc_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct solo_enc_fh *fh = file->private_data; - - return videobuf_poll_stream(file, &fh->vidq, wait); -} - -static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct solo_enc_fh *fh = file->private_data; - - return videobuf_mmap_mapper(&fh->vidq, vma); -} - -static int solo_enc_open(struct file *file) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_enc_fh *fh; - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (fh == NULL) - return -ENOMEM; - - fh->enc = solo_enc; - file->private_data = fh; - INIT_LIST_HEAD(&fh->vidq_active); - fh->fmt = V4L2_PIX_FMT_MPEG; - fh->type = SOLO_ENC_TYPE_STD; - - videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops, - &solo_enc->solo_dev->pdev->dev, - &solo_enc->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct videobuf_buffer), fh, NULL); - - return 0; -} - -static ssize_t solo_enc_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct solo_enc_fh *fh = file->private_data; - struct solo_enc_dev *solo_enc = fh->enc; - - /* Make sure the encoder is on */ - if (!fh->enc_on) { - int ret; - - spin_lock(&solo_enc->lock); - ret = solo_enc_on(fh); - spin_unlock(&solo_enc->lock); - if (ret) - return ret; - - ret = solo_start_fh_thread(fh); - if (ret) - return ret; - } - - return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static int solo_enc_release(struct file *file) -{ - struct solo_enc_fh *fh = file->private_data; - struct solo_enc_dev *solo_enc = fh->enc; - - videobuf_stop(&fh->vidq); - videobuf_mmap_free(&fh->vidq); - - spin_lock(&solo_enc->lock); - solo_enc_off(fh); - spin_unlock(&solo_enc->lock); - - kfree(fh); - - return 0; -} - -static int solo_enc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - - strcpy(cap->driver, SOLO6010_NAME); - snprintf(cap->card, sizeof(cap->card), "Softlogic 6010 Enc %d", - solo_enc->ch); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", - pci_name(solo_dev->pdev)); - cap->version = SOLO6010_VER_NUM; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - return 0; -} - -static int solo_enc_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - - if (input->index) - return -EINVAL; - - snprintf(input->name, sizeof(input->name), "Encoder %d", - solo_enc->ch + 1); - input->type = V4L2_INPUT_TYPE_CAMERA; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - input->std = V4L2_STD_NTSC_M; - else - input->std = V4L2_STD_PAL_B; - - if (!tw28_get_video_status(solo_dev, solo_enc->ch)) - input->status = V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int solo_enc_set_input(struct file *file, void *priv, unsigned int index) -{ - if (index) - return -EINVAL; - - return 0; -} - -static int solo_enc_get_input(struct file *file, void *priv, - unsigned int *index) -{ - *index = 0; - - return 0; -} - -static int solo_enc_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - switch (f->index) { - case 0: - f->pixelformat = V4L2_PIX_FMT_MPEG; - strcpy(f->description, "MPEG-4 AVC"); - break; - case 1: - f->pixelformat = V4L2_PIX_FMT_MJPEG; - strcpy(f->description, "MJPEG"); - break; - default: - return -EINVAL; - } - - f->flags = V4L2_FMT_FLAG_COMPRESSED; - - return 0; -} - -static int solo_enc_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - - if (pix->pixelformat != V4L2_PIX_FMT_MPEG && - pix->pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - /* We cannot change width/height in mid read */ - if (atomic_read(&solo_enc->readers) > 0) { - if (pix->width != solo_enc->width || - pix->height != solo_enc->height) - return -EBUSY; - } - - if (pix->width < solo_dev->video_hsize || - pix->height < solo_dev->video_vsize << 1) { - /* Default to CIF 1/2 size */ - pix->width = solo_dev->video_hsize >> 1; - pix->height = solo_dev->video_vsize; - } else { - /* Full frame */ - pix->width = solo_dev->video_hsize; - pix->height = solo_dev->video_vsize << 1; - } - - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_INTERLACED; - else if (pix->field != V4L2_FIELD_INTERLACED) - pix->field = V4L2_FIELD_INTERLACED; - - /* Just set these */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->sizeimage = FRAME_BUF_SIZE; - - return 0; -} - -static int solo_enc_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - spin_lock(&solo_enc->lock); - - ret = solo_enc_try_fmt_cap(file, priv, f); - if (ret) { - spin_unlock(&solo_enc->lock); - return ret; - } - - if (pix->width == solo_dev->video_hsize) - solo_enc->mode = SOLO_ENC_MODE_D1; - else - solo_enc->mode = SOLO_ENC_MODE_CIF; - - /* This does not change the encoder at all */ - fh->fmt = pix->pixelformat; - - if (pix->priv) - fh->type = SOLO_ENC_TYPE_EXT; - ret = solo_enc_on(fh); - - spin_unlock(&solo_enc->lock); - - if (ret) - return ret; - - return solo_start_fh_thread(fh); -} - -static int solo_enc_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_enc->width; - pix->height = solo_enc->height; - pix->pixelformat = fh->fmt; - pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : - V4L2_FIELD_NONE; - pix->sizeimage = FRAME_BUF_SIZE; - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int solo_enc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_reqbufs(&fh->vidq, req); -} - -static int solo_enc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_querybuf(&fh->vidq, buf); -} - -static int solo_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_qbuf(&fh->vidq, buf); -} - -static int solo_enc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int ret; - - /* Make sure the encoder is on */ - if (!fh->enc_on) { - spin_lock(&solo_enc->lock); - ret = solo_enc_on(fh); - spin_unlock(&solo_enc->lock); - if (ret) - return ret; - - ret = solo_start_fh_thread(fh); - if (ret) - return ret; - } - - ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); - if (ret) - return ret; - - /* Signal motion detection */ - if (solo_is_motion_on(solo_enc)) { - buf->flags |= V4L2_BUF_FLAG_MOTION_ON; - if (solo_enc->motion_detected) { - buf->flags |= V4L2_BUF_FLAG_MOTION_DETECTED; - solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, - 1 << solo_enc->ch); - solo_enc->motion_detected = 0; - } - } - - /* Check for key frame on mpeg data */ - if (fh->fmt == V4L2_PIX_FMT_MPEG) { - struct videobuf_dmabuf *vbuf = - videobuf_to_dma(fh->vidq.bufs[buf->index]); - - if (vbuf) { - u8 *p = sg_virt(vbuf->sglist); - if (p[3] == 0x00) - buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - else - buf->flags |= V4L2_BUF_FLAG_PFRAME; - } - } - - return 0; -} - -static int solo_enc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct solo_enc_fh *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamon(&fh->vidq); -} - -static int solo_enc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct solo_enc_fh *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vidq); -} - -static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - return 0; -} - -static int solo_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct solo_enc_fh *fh = priv; - struct solo6010_dev *solo_dev = fh->enc->solo_dev; - - if (fsize->pixel_format != V4L2_PIX_FMT_MPEG) - return -EINVAL; - - switch (fsize->index) { - case 0: - fsize->discrete.width = solo_dev->video_hsize >> 1; - fsize->discrete.height = solo_dev->video_vsize; - break; - case 1: - fsize->discrete.width = solo_dev->video_hsize; - fsize->discrete.height = solo_dev->video_vsize << 1; - break; - default: - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - - return 0; -} - -static int solo_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fintv) -{ - struct solo_enc_fh *fh = priv; - struct solo6010_dev *solo_dev = fh->enc->solo_dev; - - if (fintv->pixel_format != V4L2_PIX_FMT_MPEG || fintv->index) - return -EINVAL; - - fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; - - fintv->stepwise.min.numerator = solo_dev->fps; - fintv->stepwise.min.denominator = 1; - - fintv->stepwise.max.numerator = solo_dev->fps; - fintv->stepwise.max.denominator = 15; - - fintv->stepwise.step.numerator = 1; - fintv->stepwise.step.denominator = 1; - - return 0; -} - -static int solo_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = solo_enc->interval; - cp->timeperframe.denominator = solo_dev->fps; - cp->capturemode = 0; - /* XXX: Shouldn't we be able to get/set this from videobuf? */ - cp->readbuffers = 2; - - return 0; -} - -static int solo_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_captureparm *cp = &sp->parm.capture; - - spin_lock(&solo_enc->lock); - - if (atomic_read(&solo_enc->readers) > 0) { - spin_unlock(&solo_enc->lock); - return -EBUSY; - } - - if ((cp->timeperframe.numerator == 0) || - (cp->timeperframe.denominator == 0)) { - /* reset framerate */ - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = solo_dev->fps; - } - - if (cp->timeperframe.denominator != solo_dev->fps) - cp->timeperframe.denominator = solo_dev->fps; - - if (cp->timeperframe.numerator > 15) - cp->timeperframe.numerator = 15; - - solo_enc->interval = cp->timeperframe.numerator; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - - solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1); - solo_update_mode(solo_enc); - - spin_unlock(&solo_enc->lock); - - return 0; -} - -static int solo_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - - qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); - if (!qc->id) - return -EINVAL; - - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0x00, 0xff, 1, 0x80); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(qc, 0x00, 0x0f, 1, 0x00); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qc, 1, 255, 1, solo_dev->fps); -#ifdef PRIVATE_CIDS - case V4L2_CID_MOTION_THRESHOLD: - qc->flags |= V4L2_CTRL_FLAG_SLIDER; - qc->type = V4L2_CTRL_TYPE_INTEGER; - qc->minimum = 0; - qc->maximum = 0xffff; - qc->step = 1; - qc->default_value = SOLO_DEF_MOT_THRESH; - strlcpy(qc->name, "Motion Detection Threshold", - sizeof(qc->name)); - return 0; - case V4L2_CID_MOTION_ENABLE: - qc->type = V4L2_CTRL_TYPE_BOOLEAN; - qc->minimum = 0; - qc->maximum = qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "Motion Detection Enable", sizeof(qc->name)); - return 0; -#else - case V4L2_CID_MOTION_THRESHOLD: - return v4l2_ctrl_query_fill(qc, 0, 0xffff, 1, - SOLO_DEF_MOT_THRESH); - case V4L2_CID_MOTION_ENABLE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); -#endif - case V4L2_CID_RDS_TX_RADIO_TEXT: - qc->type = V4L2_CTRL_TYPE_STRING; - qc->minimum = 0; - qc->maximum = OSD_TEXT_MAX; - qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "OSD Text", sizeof(qc->name)); - return 0; - } - - return -EINVAL; -} - -static int solo_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - struct v4l2_queryctrl qctrl; - int err; - - qctrl.id = qmenu->id; - err = solo_queryctrl(file, priv, &qctrl); - if (err) - return err; - - return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); -} - -static int solo_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_SHARPNESS: - return tw28_get_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, - &ctrl->value); - case V4L2_CID_MPEG_VIDEO_ENCODING: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = solo_enc->gop; - break; - case V4L2_CID_MOTION_THRESHOLD: - ctrl->value = solo_enc->motion_thresh; - break; - case V4L2_CID_MOTION_ENABLE: - ctrl->value = solo_is_motion_on(solo_enc); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int solo_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo6010_dev *solo_dev = solo_enc->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_SHARPNESS: - return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, - ctrl->value); - case V4L2_CID_MPEG_VIDEO_ENCODING: - if (ctrl->value != V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) - return -ERANGE; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - if (ctrl->value < 1 || ctrl->value > 255) - return -ERANGE; - solo_enc->gop = ctrl->value; - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch), - solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch), - solo_enc->gop); - break; - case V4L2_CID_MOTION_THRESHOLD: - /* TODO accept value on lower 16-bits and use high - * 16-bits to assign the value to a specific block */ - if (ctrl->value < 0 || ctrl->value > 0xffff) - return -ERANGE; - solo_enc->motion_thresh = ctrl->value; - solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->value); - break; - case V4L2_CID_MOTION_ENABLE: - solo_motion_toggle(solo_enc, ctrl->value); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int solo_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int i; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = (ctrls->controls + i); - int err; - - switch (ctrl->id) { - case V4L2_CID_RDS_TX_RADIO_TEXT: - if (ctrl->size - 1 > OSD_TEXT_MAX) - err = -ERANGE; - else { - err = copy_from_user(solo_enc->osd_text, - ctrl->string, - OSD_TEXT_MAX); - solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; - if (!err) - err = solo_osd_print(solo_enc); - } - break; - default: - err = -EINVAL; - } - - if (err < 0) { - ctrls->error_idx = i; - return err; - } - } - - return 0; -} - -static int solo_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int i; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = (ctrls->controls + i); - int err; - - switch (ctrl->id) { - case V4L2_CID_RDS_TX_RADIO_TEXT: - if (ctrl->size < OSD_TEXT_MAX) { - ctrl->size = OSD_TEXT_MAX; - err = -ENOSPC; - } else { - err = copy_to_user(ctrl->string, - solo_enc->osd_text, - OSD_TEXT_MAX); - } - break; - default: - err = -EINVAL; - } - - if (err < 0) { - ctrls->error_idx = i; - return err; - } - } - - return 0; -} - -static const struct v4l2_file_operations solo_enc_fops = { - .owner = THIS_MODULE, - .open = solo_enc_open, - .release = solo_enc_release, - .read = solo_enc_read, - .poll = solo_enc_poll, - .mmap = solo_enc_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { - .vidioc_querycap = solo_enc_querycap, - .vidioc_s_std = solo_enc_s_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enc_enum_input, - .vidioc_s_input = solo_enc_set_input, - .vidioc_g_input = solo_enc_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = solo_enc_reqbufs, - .vidioc_querybuf = solo_enc_querybuf, - .vidioc_qbuf = solo_enc_qbuf, - .vidioc_dqbuf = solo_enc_dqbuf, - .vidioc_streamon = solo_enc_streamon, - .vidioc_streamoff = solo_enc_streamoff, - /* Frame size and interval */ - .vidioc_enum_framesizes = solo_enum_framesizes, - .vidioc_enum_frameintervals = solo_enum_frameintervals, - /* Video capture parameters */ - .vidioc_s_parm = solo_s_parm, - .vidioc_g_parm = solo_g_parm, - /* Controls */ - .vidioc_queryctrl = solo_queryctrl, - .vidioc_querymenu = solo_querymenu, - .vidioc_g_ctrl = solo_g_ctrl, - .vidioc_s_ctrl = solo_s_ctrl, - .vidioc_g_ext_ctrls = solo_g_ext_ctrls, - .vidioc_s_ext_ctrls = solo_s_ext_ctrls, -}; - -static struct video_device solo_enc_template = { - .name = SOLO6010_NAME, - .fops = &solo_enc_fops, - .ioctl_ops = &solo_enc_ioctl_ops, - .minor = -1, - .release = video_device_release, - - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, - .current_norm = V4L2_STD_NTSC_M, -}; - -static struct solo_enc_dev *solo_enc_alloc(struct solo6010_dev *solo_dev, u8 ch) -{ - struct solo_enc_dev *solo_enc; - int ret; - - solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); - if (!solo_enc) - return ERR_PTR(-ENOMEM); - - solo_enc->vfd = video_device_alloc(); - if (!solo_enc->vfd) { - kfree(solo_enc); - return ERR_PTR(-ENOMEM); - } - - solo_enc->solo_dev = solo_dev; - solo_enc->ch = ch; - - *solo_enc->vfd = solo_enc_template; - solo_enc->vfd->parent = &solo_dev->pdev->dev; - ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, - video_nr); - if (ret < 0) { - video_device_release(solo_enc->vfd); - kfree(solo_enc); - return ERR_PTR(ret); - } - - video_set_drvdata(solo_enc->vfd, solo_enc); - - snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), - "%s-enc (%i/%i)", SOLO6010_NAME, solo_dev->vfd->num, - solo_enc->vfd->num); - - if (video_nr != -1) - video_nr++; - - spin_lock_init(&solo_enc->lock); - init_waitqueue_head(&solo_enc->thread_wait); - atomic_set(&solo_enc->readers, 0); - - solo_enc->qp = SOLO_DEFAULT_QP; - solo_enc->gop = solo_dev->fps; - solo_enc->interval = 1; - solo_enc->mode = SOLO_ENC_MODE_CIF; - solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; - - spin_lock(&solo_enc->lock); - solo_update_mode(solo_enc); - spin_unlock(&solo_enc->lock); - - return solo_enc; -} - -static void solo_enc_free(struct solo_enc_dev *solo_enc) -{ - if (solo_enc == NULL) - return; - - video_unregister_device(solo_enc->vfd); - kfree(solo_enc); -} - -int solo_enc_v4l2_init(struct solo6010_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i); - if (IS_ERR(solo_dev->v4l2_enc[i])) - break; - } - - if (i != solo_dev->nr_chans) { - int ret = PTR_ERR(solo_dev->v4l2_enc[i]); - while (i--) - solo_enc_free(solo_dev->v4l2_enc[i]); - return ret; - } - - /* D1@MAX-FPS * 4 */ - solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; - - dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", - solo_dev->v4l2_enc[0]->vfd->num, - solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); - - return 0; -} - -void solo_enc_v4l2_exit(struct solo6010_dev *solo_dev) -{ - int i; - - solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_enc_free(solo_dev->v4l2_enc[i]); -} diff --git a/drivers/staging/solo6x10/solo6010-v4l2.c b/drivers/staging/solo6x10/solo6010-v4l2.c deleted file mode 100644 index 4e24e92..0000000 --- a/drivers/staging/solo6x10/solo6010-v4l2.c +++ /dev/null @@ -1,966 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "solo6010.h" -#include "solo6010-tw28.h" - -#define SOLO_HW_BPL 2048 -#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED - -/* Image size is two fields, SOLO_HW_BPL is one horizontal line */ -#define solo_vlines(__solo) (__solo->video_vsize * 2) -#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ - solo_vlines(__solo)) -#define solo_bytesperline(__solo) (__solo->video_hsize * 2) - -#define MIN_VID_BUFFERS 4 - -/* Simple file handle */ -struct solo_filehandle { - struct solo6010_dev *solo_dev; - struct videobuf_queue vidq; - struct task_struct *kthread; - spinlock_t slock; - int old_write; - struct list_head vidq_active; - struct p2m_desc desc[SOLO_NR_P2M_DESC]; - int desc_idx; -}; - -unsigned video_nr = -1; -module_param(video_nr, uint, 0644); -MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); - -static void erase_on(struct solo6010_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - solo_dev->erasing = 1; - solo_dev->frame_blank = 0; -} - -static int erase_off(struct solo6010_dev *solo_dev) -{ - if (!solo_dev->erasing) - return 0; - - /* First time around, assert erase off */ - if (!solo_dev->frame_blank) - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); - /* Keep the erasing flag on for 8 frames minimum */ - if (solo_dev->frame_blank++ >= 8) - solo_dev->erasing = 0; - - return 1; -} - -void solo_video_in_isr(struct solo6010_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN); - wake_up_interruptible(&solo_dev->disp_thread_wait); -} - -static void solo_win_setup(struct solo6010_dev *solo_dev, u8 ch, - int sx, int sy, int ex, int ey, int scale) -{ - if (ch >= solo_dev->nr_chans) - return; - - /* Here, we just keep window/channel the same */ - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), - SOLO_VI_WIN_CHANNEL(ch) | - SOLO_VI_WIN_SX(sx) | - SOLO_VI_WIN_EX(ex) | - SOLO_VI_WIN_SCALE(scale)); - - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), - SOLO_VI_WIN_SY(sy) | - SOLO_VI_WIN_EY(ey)); -} - -static int solo_v4l2_ch_ext_4up(struct solo6010_dev *solo_dev, u8 idx, int on) -{ - u8 ch = idx * 4; - - if (ch >= solo_dev->nr_chans) - return -EINVAL; - - if (!on) { - u8 i; - for (i = ch; i < ch + 4; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - /* Row 1 */ - solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, 3); - solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, - solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); - /* Row 2 */ - solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, - solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); - solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, solo_dev->video_hsize, - solo_vlines(solo_dev), 3); - - return 0; -} - -static int solo_v4l2_ch_ext_16up(struct solo6010_dev *solo_dev, int on) -{ - int sy, ysize, hsize, i; - - if (!on) { - for (i = 0; i < 16; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - ysize = solo_vlines(solo_dev) / 4; - hsize = solo_dev->video_hsize / 4; - - for (sy = 0, i = 0; i < 4; i++, sy += ysize) { - solo_win_setup(solo_dev, i * 4, 0, sy, hsize, - sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, - hsize * 2, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, - hsize * 3, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, - solo_dev->video_hsize, sy + ysize, 5); - } - - return 0; -} - -static int solo_v4l2_ch(struct solo6010_dev *solo_dev, u8 ch, int on) -{ - u8 ext_ch; - - if (ch < solo_dev->nr_chans) { - solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, - on ? 0 : solo_vlines(solo_dev), - solo_dev->video_hsize, solo_vlines(solo_dev), - on ? 1 : 0); - return 0; - } - - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - ext_ch = ch - solo_dev->nr_chans; - - /* 4up's first */ - if (ext_ch < 4) - return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); - - /* Remaining case is 16up for 16-port */ - return solo_v4l2_ch_ext_16up(solo_dev, on); -} - -static int solo_v4l2_set_ch(struct solo6010_dev *solo_dev, u8 ch) -{ - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - erase_on(solo_dev); - - solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); - solo_v4l2_ch(solo_dev, ch, 1); - - solo_dev->cur_disp_ch = ch; - - return 0; -} - -static void disp_reset_desc(struct solo_filehandle *fh) -{ - /* We use desc mode, which ignores desc 0 */ - memset(fh->desc, 0, sizeof(*fh->desc)); - fh->desc_idx = 1; -} - -static int disp_flush_descs(struct solo_filehandle *fh) -{ - int ret; - - if (!fh->desc_idx) - return 0; - - ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP, - fh->desc, fh->desc_idx); - disp_reset_desc(fh); - - return ret; -} - -static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr, - u32 ext_addr, int size, int repeat, int ext_size) -{ - if (fh->desc_idx >= SOLO_NR_P2M_DESC) { - int ret = disp_flush_descs(fh); - if (ret) - return ret; - } - - solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr, - size, repeat, ext_size); - fh->desc_idx++; - - return 0; -} - -static void solo_fillbuf(struct solo_filehandle *fh, - struct videobuf_buffer *vb) -{ - struct solo6010_dev *solo_dev = fh->solo_dev; - struct videobuf_dmabuf *vbuf; - unsigned int fdma_addr; - int error = 1; - int i; - struct scatterlist *sg; - dma_addr_t sg_dma; - int sg_size_left; - - vbuf = videobuf_to_dma(vb); - if (!vbuf) - goto finish_buf; - - if (erase_off(solo_dev)) { - int i; - - /* Just blit to the entire sg list, ignoring size */ - for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) { - void *p = sg_virt(sg); - size_t len = sg_dma_len(sg); - - for (i = 0; i < len; i += 2) { - ((u8 *)p)[i] = 0x80; - ((u8 *)p)[i + 1] = 0x00; - } - } - - error = 0; - goto finish_buf; - } - - disp_reset_desc(fh); - sg = vbuf->sglist; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - - fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write * - (SOLO_HW_BPL * solo_vlines(solo_dev))); - - for (i = 0; i < solo_vlines(solo_dev); i++) { - int line_len = solo_bytesperline(solo_dev); - int lines; - - if (!sg_size_left) { - sg = sg_next(sg); - if (sg == NULL) - goto finish_buf; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - } - - /* No room for an entire line, so chunk it up */ - if (sg_size_left < line_len) { - int this_addr = fdma_addr; - - while (line_len > 0) { - int this_write; - - if (!sg_size_left) { - sg = sg_next(sg); - if (sg == NULL) - goto finish_buf; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - } - - this_write = min(sg_size_left, line_len); - - if (disp_push_desc(fh, sg_dma, this_addr, - this_write, 0, 0)) - goto finish_buf; - - line_len -= this_write; - sg_size_left -= this_write; - sg_dma += this_write; - this_addr += this_write; - } - - fdma_addr += SOLO_HW_BPL; - continue; - } - - /* Shove as many lines into a repeating descriptor as possible */ - lines = min(sg_size_left / line_len, - solo_vlines(solo_dev) - i); - - if (disp_push_desc(fh, sg_dma, fdma_addr, line_len, - lines - 1, SOLO_HW_BPL)) - goto finish_buf; - - i += lines - 1; - fdma_addr += SOLO_HW_BPL * lines; - sg_dma += lines * line_len; - sg_size_left -= lines * line_len; - } - - error = disp_flush_descs(fh); - -finish_buf: - if (error) { - vb->state = VIDEOBUF_ERROR; - } else { - vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev); - vb->state = VIDEOBUF_DONE; - vb->field_count++; - do_gettimeofday(&vb->ts); - } - - wake_up(&vb->done); - - return; -} - -static void solo_thread_try(struct solo_filehandle *fh) -{ - struct videobuf_buffer *vb; - unsigned int cur_write; - - for (;;) { - spin_lock(&fh->slock); - - if (list_empty(&fh->vidq_active)) - break; - - vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer, - queue); - - if (!waitqueue_active(&vb->done)) - break; - - cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev, - SOLO_VI_STATUS0)); - if (cur_write == fh->old_write) - break; - - fh->old_write = cur_write; - list_del(&vb->queue); - - spin_unlock(&fh->slock); - - solo_fillbuf(fh, vb); - } - - assert_spin_locked(&fh->slock); - spin_unlock(&fh->slock); -} - -static int solo_thread(void *data) -{ - struct solo_filehandle *fh = data; - struct solo6010_dev *solo_dev = fh->solo_dev; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_dev->disp_thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_thread_try(fh); - try_to_freeze(); - } - - remove_wait_queue(&solo_dev->disp_thread_wait, &wait); - - return 0; -} - -static int solo_start_thread(struct solo_filehandle *fh) -{ - fh->kthread = kthread_run(solo_thread, fh, SOLO6010_NAME "_disp"); - - if (IS_ERR(fh->kthread)) - return PTR_ERR(fh->kthread); - - return 0; -} - -static void solo_stop_thread(struct solo_filehandle *fh) -{ - if (fh->kthread) { - kthread_stop(fh->kthread); - fh->kthread = NULL; - } -} - -static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo6010_dev *solo_dev = fh->solo_dev; - - *size = solo_image_size(solo_dev); - - if (*count < MIN_VID_BUFFERS) - *count = MIN_VID_BUFFERS; - - return 0; -} - -static int solo_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo6010_dev *solo_dev = fh->solo_dev; - - vb->size = solo_image_size(solo_dev); - if (vb->baddr != 0 && vb->bsize < vb->size) - return -EINVAL; - - /* XXX: These properties only change when queue is idle */ - vb->width = solo_dev->video_hsize; - vb->height = solo_vlines(solo_dev); - vb->bytesperline = solo_bytesperline(solo_dev); - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int rc = videobuf_iolock(vq, vb, NULL); - if (rc < 0) { - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; - return rc; - } - } - vb->state = VIDEOBUF_PREPARED; - - return 0; -} - -static void solo_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo6010_dev *solo_dev = fh->solo_dev; - - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &fh->vidq_active); - wake_up_interruptible(&solo_dev->disp_thread_wait); -} - -static void solo_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops solo_video_qops = { - .buf_setup = solo_buf_setup, - .buf_prepare = solo_buf_prepare, - .buf_queue = solo_buf_queue, - .buf_release = solo_buf_release, -}; - -static unsigned int solo_v4l2_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_poll_stream(file, &fh->vidq, wait); -} - -static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_mmap_mapper(&fh->vidq, vma); -} - -static int solo_v4l2_open(struct file *file) -{ - struct solo6010_dev *solo_dev = video_drvdata(file); - struct solo_filehandle *fh; - int ret; - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (fh == NULL) - return -ENOMEM; - - spin_lock_init(&fh->slock); - INIT_LIST_HEAD(&fh->vidq_active); - fh->solo_dev = solo_dev; - file->private_data = fh; - - ret = solo_start_thread(fh); - if (ret) { - kfree(fh); - return ret; - } - - videobuf_queue_sg_init(&fh->vidq, &solo_video_qops, - &solo_dev->pdev->dev, &fh->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - SOLO_DISP_PIX_FIELD, - sizeof(struct videobuf_buffer), fh, NULL); - - return 0; -} - -static ssize_t solo_v4l2_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static int solo_v4l2_release(struct file *file) -{ - struct solo_filehandle *fh = file->private_data; - - videobuf_stop(&fh->vidq); - videobuf_mmap_free(&fh->vidq); - solo_stop_thread(fh); - kfree(fh); - - return 0; -} - -static int solo_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_filehandle *fh = priv; - struct solo6010_dev *solo_dev = fh->solo_dev; - - strcpy(cap->driver, SOLO6010_NAME); - strcpy(cap->card, "Softlogic 6010"); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", - pci_name(solo_dev->pdev)); - cap->version = SOLO6010_VER_NUM; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - return 0; -} - -static int solo_enum_ext_input(struct solo6010_dev *solo_dev, - struct v4l2_input *input) -{ - static const char *dispnames_1[] = { "4UP" }; - static const char *dispnames_2[] = { "4UP-1", "4UP-2" }; - static const char *dispnames_5[] = { - "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" - }; - const char **dispnames; - - if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) - return -EINVAL; - - if (solo_dev->nr_ext == 5) - dispnames = dispnames_5; - else if (solo_dev->nr_ext == 2) - dispnames = dispnames_2; - else - dispnames = dispnames_1; - - snprintf(input->name, sizeof(input->name), "Multi %s", - dispnames[input->index - solo_dev->nr_chans]); - - return 0; -} - -static int solo_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_filehandle *fh = priv; - struct solo6010_dev *solo_dev = fh->solo_dev; - - if (input->index >= solo_dev->nr_chans) { - int ret = solo_enum_ext_input(solo_dev, input); - if (ret < 0) - return ret; - } else { - snprintf(input->name, sizeof(input->name), "Camera %d", - input->index + 1); - - /* We can only check this for normal inputs */ - if (!tw28_get_video_status(solo_dev, input->index)) - input->status = V4L2_IN_ST_NO_SIGNAL; - } - - input->type = V4L2_INPUT_TYPE_CAMERA; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - input->std = V4L2_STD_NTSC_M; - else - input->std = V4L2_STD_PAL_B; - - return 0; -} - -static int solo_set_input(struct file *file, void *priv, unsigned int index) -{ - struct solo_filehandle *fh = priv; - - return solo_v4l2_set_ch(fh->solo_dev, index); -} - -static int solo_get_input(struct file *file, void *priv, unsigned int *index) -{ - struct solo_filehandle *fh = priv; - - *index = fh->solo_dev->cur_disp_ch; - - return 0; -} - -static int solo_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_UYVY; - strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); - - return 0; -} - -static int solo_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - struct solo6010_dev *solo_dev = fh->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - int image_size = solo_image_size(solo_dev); - - /* Check supported sizes */ - if (pix->width != solo_dev->video_hsize) - pix->width = solo_dev->video_hsize; - if (pix->height != solo_vlines(solo_dev)) - pix->height = solo_vlines(solo_dev); - if (pix->sizeimage != image_size) - pix->sizeimage = image_size; - - /* Check formats */ - if (pix->field == V4L2_FIELD_ANY) - pix->field = SOLO_DISP_PIX_FIELD; - - if (pix->pixelformat != V4L2_PIX_FMT_UYVY || - pix->field != SOLO_DISP_PIX_FIELD || - pix->colorspace != V4L2_COLORSPACE_SMPTE170M) - return -EINVAL; - - return 0; -} - -static int solo_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - - if (videobuf_queue_is_busy(&fh->vidq)) - return -EBUSY; - - /* For right now, if it doesn't match our running config, - * then fail */ - return solo_try_fmt_cap(file, priv, f); -} - -static int solo_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - struct solo6010_dev *solo_dev = fh->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_dev->video_hsize; - pix->height = solo_vlines(solo_dev); - pix->pixelformat = V4L2_PIX_FMT_UYVY; - pix->field = SOLO_DISP_PIX_FIELD; - pix->sizeimage = solo_image_size(solo_dev); - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->bytesperline = solo_bytesperline(solo_dev); - - return 0; -} - -static int solo_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) -{ - struct solo_filehandle *fh = priv; - - return videobuf_reqbufs(&fh->vidq, req); -} - -static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_querybuf(&fh->vidq, buf); -} - -static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_qbuf(&fh->vidq, buf); -} - -static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); -} - -static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct solo_filehandle *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamon(&fh->vidq); -} - -static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct solo_filehandle *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vidq); -} - -static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - return 0; -} - -static const u32 solo_motion_ctrls[] = { - V4L2_CID_MOTION_TRACE, - 0 -}; - -static const u32 *solo_ctrl_classes[] = { - solo_motion_ctrls, - NULL -}; - -static int solo_disp_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); - if (!qc->id) - return -EINVAL; - - switch (qc->id) { -#ifdef PRIVATE_CIDS - case V4L2_CID_MOTION_TRACE: - qc->type = V4L2_CTRL_TYPE_BOOLEAN; - qc->minimum = 0; - qc->maximum = qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name)); - return 0; -#else - case V4L2_CID_MOTION_TRACE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); -#endif - } - return -EINVAL; -} - -static int solo_disp_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_filehandle *fh = priv; - struct solo6010_dev *solo_dev = fh->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_MOTION_TRACE: - ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR) - ? 1 : 0; - return 0; - } - return -EINVAL; -} - -static int solo_disp_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_filehandle *fh = priv; - struct solo6010_dev *solo_dev = fh->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_MOTION_TRACE: - if (ctrl->value) { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, - SOLO_VI_MOTION_Y_ADD | - SOLO_VI_MOTION_Y_VALUE(0x20) | - SOLO_VI_MOTION_CB_VALUE(0x10) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, - SOLO_VI_MOTION_CR_ADD | - SOLO_VI_MOTION_Y_VALUE(0x10) | - SOLO_VI_MOTION_CB_VALUE(0x80) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - } else { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); - } - return 0; - } - return -EINVAL; -} - -static const struct v4l2_file_operations solo_v4l2_fops = { - .owner = THIS_MODULE, - .open = solo_v4l2_open, - .release = solo_v4l2_release, - .read = solo_v4l2_read, - .poll = solo_v4l2_poll, - .mmap = solo_v4l2_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { - .vidioc_querycap = solo_querycap, - .vidioc_s_std = solo_s_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enum_input, - .vidioc_s_input = solo_set_input, - .vidioc_g_input = solo_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = solo_reqbufs, - .vidioc_querybuf = solo_querybuf, - .vidioc_qbuf = solo_qbuf, - .vidioc_dqbuf = solo_dqbuf, - .vidioc_streamon = solo_streamon, - .vidioc_streamoff = solo_streamoff, - /* Controls */ - .vidioc_queryctrl = solo_disp_queryctrl, - .vidioc_g_ctrl = solo_disp_g_ctrl, - .vidioc_s_ctrl = solo_disp_s_ctrl, -}; - -static struct video_device solo_v4l2_template = { - .name = SOLO6010_NAME, - .fops = &solo_v4l2_fops, - .ioctl_ops = &solo_v4l2_ioctl_ops, - .minor = -1, - .release = video_device_release, - - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, - .current_norm = V4L2_STD_NTSC_M, -}; - -int solo_v4l2_init(struct solo6010_dev *solo_dev) -{ - int ret; - int i; - - init_waitqueue_head(&solo_dev->disp_thread_wait); - - solo_dev->vfd = video_device_alloc(); - if (!solo_dev->vfd) - return -ENOMEM; - - *solo_dev->vfd = solo_v4l2_template; - solo_dev->vfd->parent = &solo_dev->pdev->dev; - - ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr); - if (ret < 0) { - video_device_release(solo_dev->vfd); - solo_dev->vfd = NULL; - return ret; - } - - video_set_drvdata(solo_dev->vfd, solo_dev); - - snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", - SOLO6010_NAME, solo_dev->vfd->num); - - if (video_nr != -1) - video_nr++; - - dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " - "%d inputs (%d extended)\n", solo_dev->vfd->num, - solo_dev->nr_chans, solo_dev->nr_ext); - - /* Cycle all the channels and clear */ - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_v4l2_set_ch(solo_dev, i); - while (erase_off(solo_dev)) - ;/* Do nothing */ - } - - /* Set the default display channel */ - solo_v4l2_set_ch(solo_dev, 0); - while (erase_off(solo_dev)) - ;/* Do nothing */ - - solo6010_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); - - return 0; -} - -void solo_v4l2_exit(struct solo6010_dev *solo_dev) -{ - solo6010_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); - if (solo_dev->vfd) { - video_unregister_device(solo_dev->vfd); - solo_dev->vfd = NULL; - } -} diff --git a/drivers/staging/solo6x10/solo6010.h b/drivers/staging/solo6x10/solo6010.h deleted file mode 100644 index 4532e12..0000000 --- a/drivers/staging/solo6x10/solo6010.h +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __SOLO6010_H -#define __SOLO6010_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "solo6010-registers.h" - -#ifndef PCI_VENDOR_ID_SOFTLOGIC -#define PCI_VENDOR_ID_SOFTLOGIC 0x9413 -#define PCI_DEVICE_ID_SOLO6010 0x6010 -#define PCI_DEVICE_ID_SOLO6110 0x6110 -#endif - -#ifndef PCI_VENDOR_ID_BLUECHERRY -#define PCI_VENDOR_ID_BLUECHERRY 0x1BB3 -/* Neugent Softlogic 6010 based cards */ -#define PCI_DEVICE_ID_NEUSOLO_4 0x4304 -#define PCI_DEVICE_ID_NEUSOLO_9 0x4309 -#define PCI_DEVICE_ID_NEUSOLO_16 0x4310 -/* Bluecherry Softlogic 6010 based cards */ -#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04 -#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09 -#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10 -/* Bluecherry Softlogic 6110 based cards */ -#define PCI_DEVICE_ID_BC_6110_4 0x5304 -#define PCI_DEVICE_ID_BC_6110_8 0x5308 -#define PCI_DEVICE_ID_BC_6110_16 0x5310 -#endif /* Bluecherry */ - -#define SOLO6010_NAME "solo6010" - -#define SOLO_MAX_CHANNELS 16 - -/* Make sure these two match */ -#define SOLO6010_VERSION "2.0.0" -#define SOLO6010_VER_MAJOR 2 -#define SOLO6010_VER_MINOR 0 -#define SOLO6010_VER_SUB 0 -#define SOLO6010_VER_NUM \ - KERNEL_VERSION(SOLO6010_VER_MAJOR, SOLO6010_VER_MINOR, SOLO6010_VER_SUB) - -#define FLAGS_6110 1 - -/* - * The SOLO6010 actually has 8 i2c channels, but we only use 2. - * 0 - Techwell chip(s) - * 1 - SAA7128 - */ -#define SOLO_I2C_ADAPTERS 2 -#define SOLO_I2C_TW 0 -#define SOLO_I2C_SAA 1 - -/* DMA Engine setup */ -#define SOLO_NR_P2M 4 -#define SOLO_NR_P2M_DESC 256 -/* MPEG and JPEG share the same interrupt and locks so they must be together - * in the same dma channel. */ -#define SOLO_P2M_DMA_ID_MP4E 0 -#define SOLO_P2M_DMA_ID_JPEG 0 -#define SOLO_P2M_DMA_ID_MP4D 1 -#define SOLO_P2M_DMA_ID_G723D 1 -#define SOLO_P2M_DMA_ID_DISP 2 -#define SOLO_P2M_DMA_ID_OSG 2 -#define SOLO_P2M_DMA_ID_G723E 3 -#define SOLO_P2M_DMA_ID_VIN 3 - -/* Encoder standard modes */ -#define SOLO_ENC_MODE_CIF 2 -#define SOLO_ENC_MODE_HD1 1 -#define SOLO_ENC_MODE_D1 9 - -#define SOLO_DEFAULT_GOP 30 -#define SOLO_DEFAULT_QP 3 - -/* There is 8MB memory available for solo to buffer MPEG4 frames. - * This gives us 512 * 16kbyte queues. */ -#define SOLO_NR_RING_BUFS 512 - -#define SOLO_CLOCK_MHZ 108 - -#ifndef V4L2_BUF_FLAG_MOTION_ON -#define V4L2_BUF_FLAG_MOTION_ON 0x0400 -#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800 -#endif -#ifndef V4L2_CID_MOTION_ENABLE -#define PRIVATE_CIDS -#define V4L2_CID_MOTION_ENABLE (V4L2_CID_PRIVATE_BASE+0) -#define V4L2_CID_MOTION_THRESHOLD (V4L2_CID_PRIVATE_BASE+1) -#define V4L2_CID_MOTION_TRACE (V4L2_CID_PRIVATE_BASE+2) -#endif - -enum SOLO_I2C_STATE { - IIC_STATE_IDLE, - IIC_STATE_START, - IIC_STATE_READ, - IIC_STATE_WRITE, - IIC_STATE_STOP -}; - -struct p2m_desc { - u32 ctrl; - u32 ext; - u32 ta; - u32 fa; -}; - -struct solo_p2m_dev { - struct mutex mutex; - struct completion completion; - int error; -}; - -#define OSD_TEXT_MAX 30 - -enum solo_enc_types { - SOLO_ENC_TYPE_STD, - SOLO_ENC_TYPE_EXT, -}; - -struct solo_enc_dev { - struct solo6010_dev *solo_dev; - /* V4L2 Items */ - struct video_device *vfd; - /* General accounting */ - wait_queue_head_t thread_wait; - spinlock_t lock; - atomic_t readers; - u8 ch; - u8 mode, gop, qp, interlaced, interval; - u8 reset_gop; - u8 bw_weight; - u8 motion_detected; - u16 motion_thresh; - u16 width; - u16 height; - char osd_text[OSD_TEXT_MAX + 1]; -}; - -struct solo_enc_buf { - u8 vop; - u8 ch; - enum solo_enc_types type; - u32 off; - u32 size; - u32 jpeg_off; - u32 jpeg_size; - struct timeval ts; -}; - -/* The SOLO6010 PCI Device */ -struct solo6010_dev { - /* General stuff */ - struct pci_dev *pdev; - u8 __iomem *reg_base; - int nr_chans; - int nr_ext; - u32 flags; - u32 irq_mask; - u32 motion_mask; - spinlock_t reg_io_lock; - - /* tw28xx accounting */ - u8 tw2865, tw2864, tw2815; - u8 tw28_cnt; - - /* i2c related items */ - struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS]; - enum SOLO_I2C_STATE i2c_state; - struct mutex i2c_mutex; - int i2c_id; - wait_queue_head_t i2c_wait; - struct i2c_msg *i2c_msg; - unsigned int i2c_msg_num; - unsigned int i2c_msg_ptr; - - /* P2M DMA Engine */ - struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; - - /* V4L2 Display items */ - struct video_device *vfd; - unsigned int erasing; - unsigned int frame_blank; - u8 cur_disp_ch; - wait_queue_head_t disp_thread_wait; - - /* V4L2 Encoder items */ - struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS]; - u16 enc_bw_remain; - /* IDX into hw mp4 encoder */ - u8 enc_idx; - /* Our software ring of enc buf references */ - u16 enc_wr_idx; - struct solo_enc_buf enc_buf[SOLO_NR_RING_BUFS]; - - /* Current video settings */ - u32 video_type; - u16 video_hsize, video_vsize; - u16 vout_hstart, vout_vstart; - u16 vin_hstart, vin_vstart; - u8 fps; - - /* Audio components */ - struct snd_card *snd_card; - struct snd_pcm *snd_pcm; - atomic_t snd_users; - int g723_hw_idx; -}; - -static inline u32 solo_reg_read(struct solo6010_dev *solo_dev, int reg) -{ - unsigned long flags; - u32 ret; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - - ret = readl(solo_dev->reg_base + reg); - rmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); - - return ret; -} - -static inline void solo_reg_write(struct solo6010_dev *solo_dev, int reg, - u32 data) -{ - unsigned long flags; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - - writel(data, solo_dev->reg_base + reg); - wmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); -} - -void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask); -void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask); - -/* Init/exit routeines for subsystems */ -int solo_disp_init(struct solo6010_dev *solo_dev); -void solo_disp_exit(struct solo6010_dev *solo_dev); - -int solo_gpio_init(struct solo6010_dev *solo_dev); -void solo_gpio_exit(struct solo6010_dev *solo_dev); - -int solo_i2c_init(struct solo6010_dev *solo_dev); -void solo_i2c_exit(struct solo6010_dev *solo_dev); - -int solo_p2m_init(struct solo6010_dev *solo_dev); -void solo_p2m_exit(struct solo6010_dev *solo_dev); - -int solo_v4l2_init(struct solo6010_dev *solo_dev); -void solo_v4l2_exit(struct solo6010_dev *solo_dev); - -int solo_enc_init(struct solo6010_dev *solo_dev); -void solo_enc_exit(struct solo6010_dev *solo_dev); - -int solo_enc_v4l2_init(struct solo6010_dev *solo_dev); -void solo_enc_v4l2_exit(struct solo6010_dev *solo_dev); - -int solo_g723_init(struct solo6010_dev *solo_dev); -void solo_g723_exit(struct solo6010_dev *solo_dev); - -/* ISR's */ -int solo_i2c_isr(struct solo6010_dev *solo_dev); -void solo_p2m_isr(struct solo6010_dev *solo_dev, int id); -void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status); -void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev); -void solo_g723_isr(struct solo6010_dev *solo_dev); -void solo_motion_isr(struct solo6010_dev *solo_dev); -void solo_video_in_isr(struct solo6010_dev *solo_dev); - -/* i2c read/write */ -u8 solo_i2c_readbyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off); -void solo_i2c_writebyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off, - u8 data); - -/* P2M DMA */ -int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size); -int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, - void *sys_addr, u32 ext_addr, u32 size); -int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id, - struct p2m_desc *pdesc, int wr, - struct scatterlist *sglist, u32 sg_off, - u32 ext_addr, u32 size); -void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, - u32 ext_addr, u32 size, int repeat, u32 ext_size); -int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id, - struct p2m_desc *desc, int desc_count); - -/* Set the threshold for motion detection */ -void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val); -#define SOLO_DEF_MOT_THRESH 0x0300 - -/* Write text on OSD */ -int solo_osd_print(struct solo_enc_dev *solo_enc); - -#endif /* __SOLO6010_H */ diff --git a/drivers/staging/solo6x10/solo6x10.h b/drivers/staging/solo6x10/solo6x10.h new file mode 100644 index 0000000..1730f4d --- /dev/null +++ b/drivers/staging/solo6x10/solo6x10.h @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SOLO6010_H +#define __SOLO6010_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "registers.h" + +#ifndef PCI_VENDOR_ID_SOFTLOGIC +#define PCI_VENDOR_ID_SOFTLOGIC 0x9413 +#define PCI_DEVICE_ID_SOLO6010 0x6010 +#define PCI_DEVICE_ID_SOLO6110 0x6110 +#endif + +#ifndef PCI_VENDOR_ID_BLUECHERRY +#define PCI_VENDOR_ID_BLUECHERRY 0x1BB3 +/* Neugent Softlogic 6010 based cards */ +#define PCI_DEVICE_ID_NEUSOLO_4 0x4304 +#define PCI_DEVICE_ID_NEUSOLO_9 0x4309 +#define PCI_DEVICE_ID_NEUSOLO_16 0x4310 +/* Bluecherry Softlogic 6010 based cards */ +#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04 +#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09 +#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10 +/* Bluecherry Softlogic 6110 based cards */ +#define PCI_DEVICE_ID_BC_6110_4 0x5304 +#define PCI_DEVICE_ID_BC_6110_8 0x5308 +#define PCI_DEVICE_ID_BC_6110_16 0x5310 +#endif /* Bluecherry */ + +#define SOLO6010_NAME "solo6010" + +#define SOLO_MAX_CHANNELS 16 + +/* Make sure these two match */ +#define SOLO6010_VERSION "2.0.0" +#define SOLO6010_VER_MAJOR 2 +#define SOLO6010_VER_MINOR 0 +#define SOLO6010_VER_SUB 0 +#define SOLO6010_VER_NUM \ + KERNEL_VERSION(SOLO6010_VER_MAJOR, SOLO6010_VER_MINOR, SOLO6010_VER_SUB) + +#define FLAGS_6110 1 + +/* + * The SOLO6010 actually has 8 i2c channels, but we only use 2. + * 0 - Techwell chip(s) + * 1 - SAA7128 + */ +#define SOLO_I2C_ADAPTERS 2 +#define SOLO_I2C_TW 0 +#define SOLO_I2C_SAA 1 + +/* DMA Engine setup */ +#define SOLO_NR_P2M 4 +#define SOLO_NR_P2M_DESC 256 +/* MPEG and JPEG share the same interrupt and locks so they must be together + * in the same dma channel. */ +#define SOLO_P2M_DMA_ID_MP4E 0 +#define SOLO_P2M_DMA_ID_JPEG 0 +#define SOLO_P2M_DMA_ID_MP4D 1 +#define SOLO_P2M_DMA_ID_G723D 1 +#define SOLO_P2M_DMA_ID_DISP 2 +#define SOLO_P2M_DMA_ID_OSG 2 +#define SOLO_P2M_DMA_ID_G723E 3 +#define SOLO_P2M_DMA_ID_VIN 3 + +/* Encoder standard modes */ +#define SOLO_ENC_MODE_CIF 2 +#define SOLO_ENC_MODE_HD1 1 +#define SOLO_ENC_MODE_D1 9 + +#define SOLO_DEFAULT_GOP 30 +#define SOLO_DEFAULT_QP 3 + +/* There is 8MB memory available for solo to buffer MPEG4 frames. + * This gives us 512 * 16kbyte queues. */ +#define SOLO_NR_RING_BUFS 512 + +#define SOLO_CLOCK_MHZ 108 + +#ifndef V4L2_BUF_FLAG_MOTION_ON +#define V4L2_BUF_FLAG_MOTION_ON 0x0400 +#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800 +#endif +#ifndef V4L2_CID_MOTION_ENABLE +#define PRIVATE_CIDS +#define V4L2_CID_MOTION_ENABLE (V4L2_CID_PRIVATE_BASE+0) +#define V4L2_CID_MOTION_THRESHOLD (V4L2_CID_PRIVATE_BASE+1) +#define V4L2_CID_MOTION_TRACE (V4L2_CID_PRIVATE_BASE+2) +#endif + +enum SOLO_I2C_STATE { + IIC_STATE_IDLE, + IIC_STATE_START, + IIC_STATE_READ, + IIC_STATE_WRITE, + IIC_STATE_STOP +}; + +struct p2m_desc { + u32 ctrl; + u32 ext; + u32 ta; + u32 fa; +}; + +struct solo_p2m_dev { + struct mutex mutex; + struct completion completion; + int error; +}; + +#define OSD_TEXT_MAX 30 + +enum solo_enc_types { + SOLO_ENC_TYPE_STD, + SOLO_ENC_TYPE_EXT, +}; + +struct solo_enc_dev { + struct solo6010_dev *solo_dev; + /* V4L2 Items */ + struct video_device *vfd; + /* General accounting */ + wait_queue_head_t thread_wait; + spinlock_t lock; + atomic_t readers; + u8 ch; + u8 mode, gop, qp, interlaced, interval; + u8 reset_gop; + u8 bw_weight; + u8 motion_detected; + u16 motion_thresh; + u16 width; + u16 height; + char osd_text[OSD_TEXT_MAX + 1]; +}; + +struct solo_enc_buf { + u8 vop; + u8 ch; + enum solo_enc_types type; + u32 off; + u32 size; + u32 jpeg_off; + u32 jpeg_size; + struct timeval ts; +}; + +/* The SOLO6010 PCI Device */ +struct solo6010_dev { + /* General stuff */ + struct pci_dev *pdev; + u8 __iomem *reg_base; + int nr_chans; + int nr_ext; + u32 flags; + u32 irq_mask; + u32 motion_mask; + spinlock_t reg_io_lock; + + /* tw28xx accounting */ + u8 tw2865, tw2864, tw2815; + u8 tw28_cnt; + + /* i2c related items */ + struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS]; + enum SOLO_I2C_STATE i2c_state; + struct mutex i2c_mutex; + int i2c_id; + wait_queue_head_t i2c_wait; + struct i2c_msg *i2c_msg; + unsigned int i2c_msg_num; + unsigned int i2c_msg_ptr; + + /* P2M DMA Engine */ + struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; + + /* V4L2 Display items */ + struct video_device *vfd; + unsigned int erasing; + unsigned int frame_blank; + u8 cur_disp_ch; + wait_queue_head_t disp_thread_wait; + + /* V4L2 Encoder items */ + struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS]; + u16 enc_bw_remain; + /* IDX into hw mp4 encoder */ + u8 enc_idx; + /* Our software ring of enc buf references */ + u16 enc_wr_idx; + struct solo_enc_buf enc_buf[SOLO_NR_RING_BUFS]; + + /* Current video settings */ + u32 video_type; + u16 video_hsize, video_vsize; + u16 vout_hstart, vout_vstart; + u16 vin_hstart, vin_vstart; + u8 fps; + + /* Audio components */ + struct snd_card *snd_card; + struct snd_pcm *snd_pcm; + atomic_t snd_users; + int g723_hw_idx; +}; + +static inline u32 solo_reg_read(struct solo6010_dev *solo_dev, int reg) +{ + unsigned long flags; + u32 ret; + u16 val; + + spin_lock_irqsave(&solo_dev->reg_io_lock, flags); + + ret = readl(solo_dev->reg_base + reg); + rmb(); + pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); + rmb(); + + spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); + + return ret; +} + +static inline void solo_reg_write(struct solo6010_dev *solo_dev, int reg, + u32 data) +{ + unsigned long flags; + u16 val; + + spin_lock_irqsave(&solo_dev->reg_io_lock, flags); + + writel(data, solo_dev->reg_base + reg); + wmb(); + pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); + rmb(); + + spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); +} + +void solo6010_irq_on(struct solo6010_dev *solo_dev, u32 mask); +void solo6010_irq_off(struct solo6010_dev *solo_dev, u32 mask); + +/* Init/exit routeines for subsystems */ +int solo_disp_init(struct solo6010_dev *solo_dev); +void solo_disp_exit(struct solo6010_dev *solo_dev); + +int solo_gpio_init(struct solo6010_dev *solo_dev); +void solo_gpio_exit(struct solo6010_dev *solo_dev); + +int solo_i2c_init(struct solo6010_dev *solo_dev); +void solo_i2c_exit(struct solo6010_dev *solo_dev); + +int solo_p2m_init(struct solo6010_dev *solo_dev); +void solo_p2m_exit(struct solo6010_dev *solo_dev); + +int solo_v4l2_init(struct solo6010_dev *solo_dev); +void solo_v4l2_exit(struct solo6010_dev *solo_dev); + +int solo_enc_init(struct solo6010_dev *solo_dev); +void solo_enc_exit(struct solo6010_dev *solo_dev); + +int solo_enc_v4l2_init(struct solo6010_dev *solo_dev); +void solo_enc_v4l2_exit(struct solo6010_dev *solo_dev); + +int solo_g723_init(struct solo6010_dev *solo_dev); +void solo_g723_exit(struct solo6010_dev *solo_dev); + +/* ISR's */ +int solo_i2c_isr(struct solo6010_dev *solo_dev); +void solo_p2m_isr(struct solo6010_dev *solo_dev, int id); +void solo_p2m_error_isr(struct solo6010_dev *solo_dev, u32 status); +void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev); +void solo_g723_isr(struct solo6010_dev *solo_dev); +void solo_motion_isr(struct solo6010_dev *solo_dev); +void solo_video_in_isr(struct solo6010_dev *solo_dev); + +/* i2c read/write */ +u8 solo_i2c_readbyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off); +void solo_i2c_writebyte(struct solo6010_dev *solo_dev, int id, u8 addr, u8 off, + u8 data); + +/* P2M DMA */ +int solo_p2m_dma_t(struct solo6010_dev *solo_dev, u8 id, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size); +int solo_p2m_dma(struct solo6010_dev *solo_dev, u8 id, int wr, + void *sys_addr, u32 ext_addr, u32 size); +int solo_p2m_dma_sg(struct solo6010_dev *solo_dev, u8 id, + struct p2m_desc *pdesc, int wr, + struct scatterlist *sglist, u32 sg_off, + u32 ext_addr, u32 size); +void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, + u32 ext_addr, u32 size, int repeat, u32 ext_size); +int solo_p2m_dma_desc(struct solo6010_dev *solo_dev, u8 id, + struct p2m_desc *desc, int desc_count); + +/* Set the threshold for motion detection */ +void solo_set_motion_threshold(struct solo6010_dev *solo_dev, u8 ch, u16 val); +#define SOLO_DEF_MOT_THRESH 0x0300 + +/* Write text on OSD */ +int solo_osd_print(struct solo_enc_dev *solo_enc); + +#endif /* __SOLO6010_H */ diff --git a/drivers/staging/solo6x10/tw28.c b/drivers/staging/solo6x10/tw28.c new file mode 100644 index 0000000..0f3d1e4 --- /dev/null +++ b/drivers/staging/solo6x10/tw28.c @@ -0,0 +1,822 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include "solo6x10.h" +#include "tw28.h" + +/* XXX: Some of these values are masked into an 8-bit regs, and shifted + * around for other 8-bit regs. What are the magic bits in these values? */ +#define DEFAULT_HDELAY_NTSC (32 - 4) +#define DEFAULT_HACTIVE_NTSC (720 + 16) +#define DEFAULT_VDELAY_NTSC (7 - 2) +#define DEFAULT_VACTIVE_NTSC (240 + 4) + +#define DEFAULT_HDELAY_PAL (32 + 4) +#define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL) +#define DEFAULT_VDELAY_PAL (6) +#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) + +static u8 tbl_tw2864_template[] = { + 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, + 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01, + 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, + 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, + 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, +}; + +static u8 tbl_tw2865_ntsc_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */ + 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, + 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ + 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, + 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, + 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, + 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ + 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8, + 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, + 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, +}; + +static u8 tbl_tw2865_pal_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */ + 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, + 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ + 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, + 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, + 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, + 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ + 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8, + 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, + 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, +}; + +#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) + +static u8 tw_readbyte(struct solo6010_dev *solo_dev, int chip_id, u8 tw6x_off, + u8 tw_off) +{ + if (is_tw286x(solo_dev, chip_id)) + return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw6x_off); + else + return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw_off); +} + +static void tw_writebyte(struct solo6010_dev *solo_dev, int chip_id, + u8 tw6x_off, u8 tw_off, u8 val) +{ + if (is_tw286x(solo_dev, chip_id)) + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw6x_off, val); + else + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw_off, val); +} + +static void tw_write_and_verify(struct solo6010_dev *solo_dev, u8 addr, u8 off, + u8 val) +{ + int i; + + for (i = 0; i < 5; i++) { + u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); + if (rval == val) + return; + + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val); + msleep_interruptible(1); + } + +/* printk("solo6010/tw28: Error writing register: %02x->%02x [%02x]\n", + addr, off, val); */ +} + +static int tw2865_setup(struct solo6010_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_tw2865_common[256]; + int i; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) + memcpy(tbl_tw2865_common, tbl_tw2865_pal_template, + sizeof(tbl_tw2865_common)); + else + memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template, + sizeof(tbl_tw2865_common)); + + /* ALINK Mode */ + if (solo_dev->nr_chans == 4) { + tbl_tw2865_common[0xd2] = 0x01; + tbl_tw2865_common[0xcf] = 0x00; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2865_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2865_common[0xcf] = 0x80; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2865_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2865_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2865_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2865_common[0xcf] = 0x80; + } + + for (i = 0; i < 0xff; i++) { + /* Skip read only registers */ + if (i >= 0xb8 && i <= 0xc1) + continue; + if ((i & ~0x30) == 0x00 || + (i & ~0x30) == 0x0c || + (i & ~0x30) == 0x0d) + continue; + if (i >= 0xc4 && i <= 0xc7) + continue; + if (i == 0xfd) + continue; + + tw_write_and_verify(solo_dev, dev_addr, i, + tbl_tw2865_common[i]); + } + + return 0; +} + +static int tw2864_setup(struct solo6010_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)]; + int i; + + memcpy(tbl_tw2864_common, tbl_tw2864_template, + sizeof(tbl_tw2864_common)); + + if (solo_dev->tw2865 == 0) { + /* IRQ Mode */ + if (solo_dev->nr_chans == 4) { + tbl_tw2864_common[0xd2] = 0x01; + tbl_tw2864_common[0xcf] = 0x00; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2864_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x40; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2864_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2864_common[0xcf] = 0x40; + } + } else { + /* ALINK Mode. Assumes that the first tw28xx is a + * 2865 and these are in cascade. */ + for (i = 0; i <= 4; i++) + tbl_tw2864_common[0x08 | i << 4] = 0x12; + + if (solo_dev->nr_chans == 8) { + tbl_tw2864_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x80; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2864_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2864_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2864_common[0xcf] = 0x80; + } + } + + /* NTSC or PAL */ + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) { + for (i = 0; i < 4; i++) { + tbl_tw2864_common[0x07 | (i << 4)] |= 0x10; + tbl_tw2864_common[0x08 | (i << 4)] |= 0x06; + tbl_tw2864_common[0x0a | (i << 4)] |= 0x08; + tbl_tw2864_common[0x0b | (i << 4)] |= 0x13; + tbl_tw2864_common[0x0e | (i << 4)] |= 0x01; + } + tbl_tw2864_common[0x9d] = 0x90; + tbl_tw2864_common[0xf3] = 0x00; + tbl_tw2864_common[0xf4] = 0xa0; + } + + for (i = 0; i < 0xff; i++) { + /* Skip read only registers */ + if (i >= 0xb8 && i <= 0xc1) + continue; + if ((i & ~0x30) == 0x00 || + (i & ~0x30) == 0x0c || + (i & ~0x30) == 0x0d) + continue; + if (i == 0x74 || i == 0x77 || i == 0x78 || + i == 0x79 || i == 0x7a) + continue; + if (i == 0xfd) + continue; + + tw_write_and_verify(solo_dev, dev_addr, i, + tbl_tw2864_common[i]); + } + + return 0; +} + +static int tw2815_setup(struct solo6010_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_ntsc_tw2815_common[] = { + 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80, + 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11, + }; + + u8 tbl_pal_tw2815_common[] = { + 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80, + 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11, + }; + + u8 tbl_tw2815_sfr[] = { + 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */ + 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */ + 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00, + 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */ + 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88, + 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */ + }; + u8 *tbl_tw2815_common; + int i; + int ch; + + tbl_ntsc_tw2815_common[0x06] = 0; + + /* Horizontal Delay Control */ + tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8); + + /* Horizontal Active Control */ + tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2); + + /* Vertical Delay Control */ + tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4); + + /* Vertical Active Control */ + tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5); + + tbl_pal_tw2815_common[0x06] = 0; + + /* Horizontal Delay Control */ + tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8); + + /* Horizontal Active Control */ + tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2); + + /* Vertical Delay Control */ + tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4); + + /* Vertical Active Control */ + tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5); + + tbl_tw2815_common = + (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ? + tbl_ntsc_tw2815_common : tbl_pal_tw2815_common; + + /* Dual ITU-R BT.656 format */ + tbl_tw2815_common[0x0d] |= 0x04; + + /* Audio configuration */ + tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6); + + if (solo_dev->nr_chans == 4) { + tbl_tw2815_sfr[0x63 - 0x40] |= 1; + tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2815_sfr[0x63 - 0x40] |= 2; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2815_sfr[0x63 - 0x40] |= 3; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; + } + + /* Output mode of R_ADATM pin (0 mixing, 1 record) */ + /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */ + + /* 8KHz, used to be 16KHz, but changed for remote client compat */ + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2; + tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2; + + /* Playback of right channel */ + tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5; + + /* Reserved value (XXX ??) */ + tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5; + + /* Analog output gain and mix ratio playback on full */ + tbl_tw2815_sfr[0x70 - 0x40] |= 0xff; + /* Select playback audio and mute all except */ + tbl_tw2815_sfr[0x71 - 0x40] |= 0x10; + tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f; + + /* End of audio configuration */ + + for (ch = 0; ch < 4; ch++) { + tbl_tw2815_common[0x0d] &= ~3; + switch (ch) { + case 0: + tbl_tw2815_common[0x0d] |= 0x21; + break; + case 1: + tbl_tw2815_common[0x0d] |= 0x20; + break; + case 2: + tbl_tw2815_common[0x0d] |= 0x23; + break; + case 3: + tbl_tw2815_common[0x0d] |= 0x22; + break; + } + + for (i = 0; i < 0x0f; i++) { + if (i == 0x00) + continue; /* read-only */ + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + dev_addr, (ch * 0x10) + i, + tbl_tw2815_common[i]); + } + } + + for (i = 0x40; i < 0x76; i++) { + /* Skip read-only and nop registers */ + if (i == 0x40 || i == 0x59 || i == 0x5a || + i == 0x5d || i == 0x5e || i == 0x5f) + continue; + + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i, + tbl_tw2815_sfr[i - 0x40]); + } + + return 0; +} + +#define FIRST_ACTIVE_LINE 0x0008 +#define LAST_ACTIVE_LINE 0x0102 + +static void saa7128_setup(struct solo6010_dev *solo_dev) +{ + int i; + unsigned char regs[128] = { + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00, + 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, + 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, + 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, + 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, + 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e, + 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77, + 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00, + 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff, + }; + + regs[0x7A] = FIRST_ACTIVE_LINE & 0xff; + regs[0x7B] = LAST_ACTIVE_LINE & 0xff; + regs[0x7C] = ((1 << 7) | + (((LAST_ACTIVE_LINE >> 8) & 1) << 6) | + (((FIRST_ACTIVE_LINE >> 8) & 1) << 4)); + + /* PAL: XXX: We could do a second set of regs to avoid this */ + if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) { + regs[0x28] = 0xE1; + + regs[0x5A] = 0x0F; + regs[0x61] = 0x02; + regs[0x62] = 0x35; + regs[0x63] = 0xCB; + regs[0x64] = 0x8A; + regs[0x65] = 0x09; + regs[0x66] = 0x2A; + + regs[0x6C] = 0xf1; + regs[0x6E] = 0x20; + + regs[0x7A] = 0x06 + 12; + regs[0x7b] = 0x24 + 12; + regs[0x7c] |= 1 << 6; + } + + /* First 0x25 bytes are read-only? */ + for (i = 0x26; i < 128; i++) { + if (i == 0x60 || i == 0x7D) + continue; + solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]); + } + + return; +} + +int solo_tw28_init(struct solo6010_dev *solo_dev) +{ + int i; + u8 value; + + /* Detect techwell chip type */ + for (i = 0; i < TW_NUM_CHIP; i++) { + value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(i), 0xFF); + + switch (value >> 3) { + case 0x18: + solo_dev->tw2865 |= 1 << i; + solo_dev->tw28_cnt++; + break; + case 0x0c: + solo_dev->tw2864 |= 1 << i; + solo_dev->tw28_cnt++; + break; + default: + value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(i), 0x59); + if ((value >> 3) == 0x04) { + solo_dev->tw2815 |= 1 << i; + solo_dev->tw28_cnt++; + } + } + } + + if (!solo_dev->tw28_cnt) + return -EINVAL; + + saa7128_setup(solo_dev); + + for (i = 0; i < solo_dev->tw28_cnt; i++) { + if ((solo_dev->tw2865 & (1 << i))) + tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + else if ((solo_dev->tw2864 & (1 << i))) + tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + else + tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + } + + dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:", + solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s"); + + if (solo_dev->tw2865) + printk(" tw2865[%d]", hweight32(solo_dev->tw2865)); + if (solo_dev->tw2864) + printk(" tw2864[%d]", hweight32(solo_dev->tw2864)); + if (solo_dev->tw2815) + printk(" tw2815[%d]", hweight32(solo_dev->tw2815)); + printk("\n"); + + return 0; +} + +/* + * We accessed the video status signal in the Techwell chip through + * iic/i2c because the video status reported by register REG_VI_STATUS1 + * (address 0x012C) of the SOLO6010 chip doesn't give the correct video + * status signal values. + */ +int tw28_get_video_status(struct solo6010_dev *solo_dev, u8 ch) +{ + u8 val, chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR, + TW_AV_STAT_ADDR) & 0x0f; + + return val & (1 << ch) ? 1 : 0; +} + +#if 0 +/* Status of audio from up to 4 techwell chips are combined into 1 variable. + * See techwell datasheet for details. */ +u16 tw28_get_audio_status(struct solo6010_dev *solo_dev) +{ + u8 val; + u16 status = 0; + int i; + + for (i = 0; i < solo_dev->tw28_cnt; i++) { + val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR, + TW_AV_STAT_ADDR) & 0xf0) >> 4; + status |= val << (i * 4); + } + + return status; +} +#endif + +int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch, + s32 val) +{ + char sval; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + if (val > 255 || val < 0) + return -ERANGE; + + switch (ctrl) { + case V4L2_CID_SHARPNESS: + /* Only 286x has sharpness */ + if (val > 0x0f || val < 0) + return -ERANGE; + if (is_tw286x(solo_dev, chip_num)) { + u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num)); + v &= 0xf0; + v |= val; + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num), v); + } else if (val != 0) + return -ERANGE; + break; + + case V4L2_CID_HUE: + if (is_tw286x(solo_dev, chip_num)) + sval = val - 128; + else + sval = (char)val; + tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), + TW_HUE_ADDR(ch), sval); + + break; + + case V4L2_CID_SATURATION: + if (is_tw286x(solo_dev, chip_num)) { + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SATURATIONU_ADDR(ch), val); + } + tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch), + TW_SATURATION_ADDR(ch), val); + + break; + + case V4L2_CID_CONTRAST: + tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch), + TW_CONTRAST_ADDR(ch), val); + break; + + case V4L2_CID_BRIGHTNESS: + if (is_tw286x(solo_dev, chip_num)) + sval = val - 128; + else + sval = (char)val; + tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch), + TW_BRIGHTNESS_ADDR(ch), sval); + + break; + default: + return -EINVAL; + } + + return 0; +} + +int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch, + s32 *val) +{ + u8 rval, chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + switch (ctrl) { + case V4L2_CID_SHARPNESS: + /* Only 286x has sharpness */ + if (is_tw286x(solo_dev, chip_num)) { + rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num)); + *val = rval & 0x0f; + } else + *val = 0; + break; + case V4L2_CID_HUE: + rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), + TW_HUE_ADDR(ch)); + if (is_tw286x(solo_dev, chip_num)) + *val = (s32)((char)rval) + 128; + else + *val = rval; + break; + case V4L2_CID_SATURATION: + *val = tw_readbyte(solo_dev, chip_num, + TW286x_SATURATIONU_ADDR(ch), + TW_SATURATION_ADDR(ch)); + break; + case V4L2_CID_CONTRAST: + *val = tw_readbyte(solo_dev, chip_num, + TW286x_CONTRAST_ADDR(ch), + TW_CONTRAST_ADDR(ch)); + break; + case V4L2_CID_BRIGHTNESS: + rval = tw_readbyte(solo_dev, chip_num, + TW286x_BRIGHTNESS_ADDR(ch), + TW_BRIGHTNESS_ADDR(ch)); + if (is_tw286x(solo_dev, chip_num)) + *val = (s32)((char)rval) + 128; + else + *val = rval; + break; + default: + return -EINVAL; + } + + return 0; +} + +#if 0 +/* + * For audio output volume, the output channel is only 1. In this case we + * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used + * is the base address of the techwell chip. + */ +void tw2815_Set_AudioOutVol(struct solo6010_dev *solo_dev, unsigned int u_val) +{ + unsigned int val; + unsigned int chip_num; + + chip_num = (solo_dev->nr_chans - 1) / 4; + + val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, + TW_AUDIO_OUTPUT_VOL_ADDR); + + u_val = (val & 0x0f) | (u_val << 4); + + tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, + TW_AUDIO_OUTPUT_VOL_ADDR, u_val); +} +#endif + +u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch) +{ + u8 val; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + val = tw_readbyte(solo_dev, chip_num, + TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch)); + + return (ch % 2) ? (val >> 4) : (val & 0x0f); +} + +void tw28_set_audio_gain(struct solo6010_dev *solo_dev, u8 ch, u8 val) +{ + u8 old_val; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + old_val = tw_readbyte(solo_dev, chip_num, + TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch)); + + val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) | + ((ch % 2) ? (val << 4) : val); + + tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch), val); +} diff --git a/drivers/staging/solo6x10/tw28.h b/drivers/staging/solo6x10/tw28.h new file mode 100644 index 0000000..4862ac9 --- /dev/null +++ b/drivers/staging/solo6x10/tw28.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SOLO6010_TW28_H +#define __SOLO6010_TW28_H + +#include "solo6x10.h" + +#define TW_NUM_CHIP 4 +#define TW_BASE_ADDR 0x28 +#define TW_CHIP_OFFSET_ADDR(n) (TW_BASE_ADDR + (n)) + +/* tw2815 */ +#define TW_AV_STAT_ADDR 0x5a +#define TW_HUE_ADDR(n) (0x07 | ((n) << 4)) +#define TW_SATURATION_ADDR(n) (0x08 | ((n) << 4)) +#define TW_CONTRAST_ADDR(n) (0x09 | ((n) << 4)) +#define TW_BRIGHTNESS_ADDR(n) (0x0a | ((n) << 4)) +#define TW_AUDIO_OUTPUT_VOL_ADDR 0x70 +#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0)) + +/* tw286x */ +#define TW286X_AV_STAT_ADDR 0xfd +#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4)) +#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4)) +#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4)) +#define TW286x_CONTRAST_ADDR(n) (0x02 | ((n) << 4)) +#define TW286x_BRIGHTNESS_ADDR(n) (0x01 | ((n) << 4)) +#define TW286x_SHARPNESS(n) (0x03 | ((n) << 4)) +#define TW286x_AUDIO_OUTPUT_VOL_ADDR 0xdf +#define TW286x_AUDIO_INPUT_GAIN_ADDR(n) (0xD0 + ((n > 1) ? 1 : 0)) + +int solo_tw28_init(struct solo6010_dev *solo_dev); + +int tw28_set_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch, + s32 val); +int tw28_get_ctrl_val(struct solo6010_dev *solo_dev, u32 ctrl, u8 ch, + s32 *val); + +u8 tw28_get_audio_gain(struct solo6010_dev *solo_dev, u8 ch); +void tw28_set_audio_gain(struct solo6010_dev *solo_dev, u8 ch, u8 val); +int tw28_get_video_status(struct solo6010_dev *solo_dev, u8 ch); + +#if 0 +unsigned int tw2815_get_audio_status(struct SOLO6010 *solo6010); +void tw2815_Set_AudioOutVol(struct SOLO6010 *solo6010, unsigned int u_val); +#endif + +#endif /* __SOLO6010_TW28_H */ diff --git a/drivers/staging/solo6x10/v4l2-enc.c b/drivers/staging/solo6x10/v4l2-enc.c new file mode 100644 index 0000000..b881923 --- /dev/null +++ b/drivers/staging/solo6x10/v4l2-enc.c @@ -0,0 +1,1825 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "solo6x10.h" +#include "tw28.h" +#include "jpeg.h" + +#define MIN_VID_BUFFERS 4 +#define FRAME_BUF_SIZE (128 * 1024) +#define MP4_QS 16 + +static int solo_enc_thread(void *data); + +extern unsigned video_nr; + +struct solo_enc_fh { + struct solo_enc_dev *enc; + u32 fmt; + u16 rd_idx; + u8 enc_on; + enum solo_enc_types type; + struct videobuf_queue vidq; + struct list_head vidq_active; + struct task_struct *kthread; + struct p2m_desc desc[SOLO_NR_P2M_DESC]; +}; + +static const u32 solo_user_ctrls[] = { + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_SHARPNESS, + 0 +}; + +static const u32 solo_mpeg_ctrls[] = { + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0 +}; + +static const u32 solo_private_ctrls[] = { + V4L2_CID_MOTION_ENABLE, + V4L2_CID_MOTION_THRESHOLD, + 0 +}; + +static const u32 solo_fmtx_ctrls[] = { + V4L2_CID_RDS_TX_RADIO_TEXT, + 0 +}; + +static const u32 *solo_ctrl_classes[] = { + solo_user_ctrls, + solo_mpeg_ctrls, + solo_fmtx_ctrls, + solo_private_ctrls, + NULL +}; + +static int solo_is_motion_on(struct solo_enc_dev *solo_enc) +{ + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + u8 ch = solo_enc->ch; + + if (solo_dev->motion_mask & (1 << ch)) + return 1; + return 0; +} + +static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) +{ + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + u8 ch = solo_enc->ch; + + spin_lock(&solo_enc->lock); + + if (on) + solo_dev->motion_mask |= (1 << ch); + else + solo_dev->motion_mask &= ~(1 << ch); + + /* Do this regardless of if we are turning on or off */ + solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, + 1 << solo_enc->ch); + solo_enc->motion_detected = 0; + + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, + SOLO_VI_MOTION_EN(solo_dev->motion_mask) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + + if (solo_dev->motion_mask) + solo6010_irq_on(solo_dev, SOLO_IRQ_MOTION); + else + solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION); + + spin_unlock(&solo_enc->lock); +} + +/* Should be called with solo_enc->lock held */ +static void solo_update_mode(struct solo_enc_dev *solo_enc) +{ + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + + assert_spin_locked(&solo_enc->lock); + + solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; + solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); + + switch (solo_enc->mode) { + case SOLO_ENC_MODE_CIF: + solo_enc->width = solo_dev->video_hsize >> 1; + solo_enc->height = solo_dev->video_vsize; + break; + case SOLO_ENC_MODE_D1: + solo_enc->width = solo_dev->video_hsize; + solo_enc->height = solo_dev->video_vsize << 1; + solo_enc->bw_weight <<= 2; + break; + default: + WARN(1, "mode is unknown\n"); + } +} + +/* Should be called with solo_enc->lock held */ +static int solo_enc_on(struct solo_enc_fh *fh) +{ + struct solo_enc_dev *solo_enc = fh->enc; + u8 ch = solo_enc->ch; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + u8 interval; + + assert_spin_locked(&solo_enc->lock); + + if (fh->enc_on) + return 0; + + solo_update_mode(solo_enc); + + /* Make sure to bw check on first reader */ + if (!atomic_read(&solo_enc->readers)) { + if (solo_enc->bw_weight > solo_dev->enc_bw_remain) + return -EBUSY; + else + solo_dev->enc_bw_remain -= solo_enc->bw_weight; + } + + fh->enc_on = 1; + fh->rd_idx = solo_enc->solo_dev->enc_wr_idx; + + if (fh->type == SOLO_ENC_TYPE_EXT) + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); + + if (atomic_inc_return(&solo_enc->readers) > 1) + return 0; + + /* Disable all encoding for this channel */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); + + /* Common for both std and ext encoding */ + solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), + solo_enc->interlaced ? 1 : 0); + + if (solo_enc->interlaced) + interval = solo_enc->interval - 1; + else + interval = solo_enc->interval; + + /* Standard encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); + + /* Extended encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); + + /* Enables the standard encoder */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); + + /* Settle down Beavis... */ + mdelay(10); + + return 0; +} + +static void solo_enc_off(struct solo_enc_fh *fh) +{ + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + + if (!fh->enc_on) + return; + + if (fh->kthread) { + kthread_stop(fh->kthread); + fh->kthread = NULL; + } + + solo_dev->enc_bw_remain += solo_enc->bw_weight; + fh->enc_on = 0; + + if (atomic_dec_return(&solo_enc->readers) > 0) + return; + + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); +} + +static int solo_start_fh_thread(struct solo_enc_fh *fh) +{ + struct solo_enc_dev *solo_enc = fh->enc; + + fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6010_NAME "_enc"); + + /* Oops, we had a problem */ + if (IS_ERR(fh->kthread)) { + spin_lock(&solo_enc->lock); + solo_enc_off(fh); + spin_unlock(&solo_enc->lock); + + return PTR_ERR(fh->kthread); + } + + return 0; +} + +static void enc_reset_gop(struct solo6010_dev *solo_dev, u8 ch) +{ + BUG_ON(ch >= solo_dev->nr_chans); + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), 1); + solo_dev->v4l2_enc[ch]->reset_gop = 1; +} + +static int enc_gop_reset(struct solo6010_dev *solo_dev, u8 ch, u8 vop) +{ + BUG_ON(ch >= solo_dev->nr_chans); + if (!solo_dev->v4l2_enc[ch]->reset_gop) + return 0; + if (vop) + return 1; + solo_dev->v4l2_enc[ch]->reset_gop = 0; + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), + solo_dev->v4l2_enc[ch]->gop); + return 0; +} + +static void enc_write_sg(struct scatterlist *sglist, void *buf, int size) +{ + struct scatterlist *sg; + u8 *src = buf; + + for (sg = sglist; sg && size > 0; sg = sg_next(sg)) { + u8 *p = sg_virt(sg); + size_t len = sg_dma_len(sg); + int i; + + for (i = 0; i < len && size; i++) + p[i] = *(src++); + } +} + +static int enc_get_mpeg_dma_sg(struct solo6010_dev *solo_dev, + struct p2m_desc *desc, + struct scatterlist *sglist, int skip, + unsigned int off, unsigned int size) +{ + int ret; + + if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) + return -EINVAL; + + if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, + desc, 0, sglist, skip, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, + sglist, skip, SOLO_MP4E_EXT_ADDR(solo_dev) + off, + SOLO_MP4E_EXT_SIZE(solo_dev) - off); + + ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, + sglist, skip + SOLO_MP4E_EXT_SIZE(solo_dev) - off, + SOLO_MP4E_EXT_ADDR(solo_dev), + size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); + + return ret; +} + +static int enc_get_mpeg_dma_t(struct solo6010_dev *solo_dev, + dma_addr_t buf, unsigned int off, + unsigned int size) +{ + int ret; + + if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) + return -EINVAL; + + if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, + SOLO_MP4E_EXT_SIZE(solo_dev) - off); + + ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, + buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off, + SOLO_MP4E_EXT_ADDR(solo_dev), + size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); + + return ret; +} + +static int enc_get_mpeg_dma(struct solo6010_dev *solo_dev, void *buf, + unsigned int off, unsigned int size) +{ + int ret; + + dma_addr_t dma_addr = pci_map_single(solo_dev->pdev, buf, size, + PCI_DMA_FROMDEVICE); + ret = enc_get_mpeg_dma_t(solo_dev, dma_addr, off, size); + pci_unmap_single(solo_dev->pdev, dma_addr, size, PCI_DMA_FROMDEVICE); + + return ret; +} + +static int enc_get_jpeg_dma_sg(struct solo6010_dev *solo_dev, + struct p2m_desc *desc, + struct scatterlist *sglist, int skip, + unsigned int off, unsigned int size) +{ + int ret; + + if (off > SOLO_JPEG_EXT_SIZE(solo_dev)) + return -EINVAL; + + if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, + desc, 0, sglist, skip, + SOLO_JPEG_EXT_ADDR(solo_dev) + off, size); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, + sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off, + SOLO_JPEG_EXT_SIZE(solo_dev) - off); + + ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, + sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off, + SOLO_JPEG_EXT_ADDR(solo_dev), + size + off - SOLO_JPEG_EXT_SIZE(solo_dev)); + + return ret; +} + +/* Returns true of __chk is within the first __range bytes of __off */ +#define OFF_IN_RANGE(__off, __range, __chk) \ + ((__off <= __chk) && ((__off + __range) >= __chk)) + +static void solo_jpeg_header(struct solo_enc_dev *solo_enc, + struct videobuf_dmabuf *vbuf) +{ + struct scatterlist *sg; + void *src = jpeg_header; + size_t copied = 0; + size_t to_copy = sizeof(jpeg_header); + + for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) { + size_t this_copy = min(sg_dma_len(sg), + (unsigned int)(to_copy - copied)); + u8 *p = sg_virt(sg); + + memcpy(p, src + copied, this_copy); + + if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5)) + p[(SOF0_START + 5) - copied] = + 0xff & (solo_enc->height >> 8); + if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6)) + p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height; + if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7)) + p[(SOF0_START + 7) - copied] = + 0xff & (solo_enc->width >> 8); + if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8)) + p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width; + + copied += this_copy; + } +} + +static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, + struct videobuf_buffer *vb, + struct videobuf_dmabuf *vbuf) +{ + struct solo6010_dev *solo_dev = fh->enc->solo_dev; + int size = enc_buf->jpeg_size; + + /* Copy the header first (direct write) */ + solo_jpeg_header(fh->enc, vbuf); + + vb->size = size + sizeof(jpeg_header); + + /* Grab the jpeg frame */ + return enc_get_jpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, + sizeof(jpeg_header), + enc_buf->jpeg_off, size); +} + +static inline int vop_interlaced(__le32 *vh) +{ + return (__le32_to_cpu(vh[0]) >> 30) & 1; +} + +static inline u32 vop_size(__le32 *vh) +{ + return __le32_to_cpu(vh[0]) & 0xFFFFF; +} + +static inline u8 vop_hsize(__le32 *vh) +{ + return (__le32_to_cpu(vh[1]) >> 8) & 0xFF; +} + +static inline u8 vop_vsize(__le32 *vh) +{ + return __le32_to_cpu(vh[1]) & 0xFF; +} + +/* must be called with *bits % 8 = 0 */ +static void write_bytes(u8 **out, unsigned *bits, const u8 *src, unsigned count) +{ + memcpy(*out, src, count); + *out += count; + *bits += count * 8; +} + +static void write_bits(u8 **out, unsigned *bits, u32 value, unsigned count) +{ + + value <<= 32 - count; // shift to the right + + while (count--) { + **out <<= 1; + **out |= !!(value & (1 << 31)); /* MSB */ + value <<= 1; + if (++(*bits) % 8 == 0) + (*out)++; + } +} + +static void write_ue(u8 **out, unsigned *bits, unsigned value) /* H.264 only */ +{ + uint32_t max = 0, cnt = 0; + + while (value > max) { + max = (max + 2) * 2 - 2; + cnt++; + } + write_bits(out, bits, 1, cnt + 1); + write_bits(out, bits, ~(max - value), cnt); +} + +static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */ +{ + if (value <= 0) + write_ue(out, bits, -value * 2); + else + write_ue(out, bits, value * 2 - 1); +} + +static void write_mpeg4_end(u8 **out, unsigned *bits) +{ + write_bits(out, bits, 0, 1); + /* align on 32-bit boundary */ + if (*bits % 32) + write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32); +} + +static void write_h264_end(u8 **out, unsigned *bits, int align) +{ + write_bits(out, bits, 1, 1); + while ((*bits) % 8) + write_bits(out, bits, 0, 1); + if (align) + while ((*bits) % 32) + write_bits(out, bits, 0, 1); +} + +static void mpeg4_write_vol(u8 **out, struct solo6010_dev *solo_dev, + __le32 *vh, unsigned fps, unsigned interval) +{ + static const u8 hdr[] = { + 0, 0, 1, 0x00 /* video_object_start_code */, + 0, 0, 1, 0x20 /* video_object_layer_start_code */ + }; + unsigned bits = 0; + unsigned width = vop_hsize(vh) << 4; + unsigned height = vop_vsize(vh) << 4; + unsigned interlaced = vop_interlaced(vh); + + write_bytes(out, &bits, hdr, sizeof(hdr)); + write_bits(out, &bits, 0, 1); /* random_accessible_vol */ + write_bits(out, &bits, 0x04, 8); /* video_object_type_indication: main */ + write_bits(out, &bits, 1, 1); /* is_object_layer_identifier */ + write_bits(out, &bits, 2, 4); /* video_object_layer_verid: table V2-39 */ + write_bits(out, &bits, 0, 3); /* video_object_layer_priority */ + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + write_bits(out, &bits, 3, 4); /* aspect_ratio_info, assuming 4:3 */ + else + write_bits(out, &bits, 2, 4); + write_bits(out, &bits, 1, 1); /* vol_control_parameters */ + write_bits(out, &bits, 1, 2); /* chroma_format: 4:2:0 */ + write_bits(out, &bits, 1, 1); /* low_delay */ + write_bits(out, &bits, 0, 1); /* vbv_parameters */ + write_bits(out, &bits, 0, 2); /* video_object_layer_shape: rectangular */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, fps, 16); /* vop_time_increment_resolution */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, 1, 1); /* fixed_vop_rate */ + write_bits(out, &bits, interval, 15); /* fixed_vop_time_increment */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, width, 13); /* video_object_layer_width */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, height, 13); /* video_object_layer_height */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, interlaced, 1); /* interlaced */ + write_bits(out, &bits, 1, 1); /* obmc_disable */ + write_bits(out, &bits, 0, 2); /* sprite_enable */ + write_bits(out, &bits, 0, 1); /* not_8_bit */ + write_bits(out, &bits, 1, 0); /* quant_type */ + write_bits(out, &bits, 0, 1); /* load_intra_quant_mat */ + write_bits(out, &bits, 0, 1); /* load_nonintra_quant_mat */ + write_bits(out, &bits, 0, 1); /* quarter_sample */ + write_bits(out, &bits, 1, 1); /* complexity_estimation_disable */ + write_bits(out, &bits, 1, 1); /* resync_marker_disable */ + write_bits(out, &bits, 0, 1); /* data_partitioned */ + write_bits(out, &bits, 0, 1); /* newpred_enable */ + write_bits(out, &bits, 0, 1); /* reduced_resolution_vop_enable */ + write_bits(out, &bits, 0, 1); /* scalability */ + write_mpeg4_end(out, &bits); +} + +static void h264_write_vol(u8 **out, struct solo6010_dev *solo_dev, __le32 *vh) +{ + static const u8 sps[] = { + 0, 0, 0, 1 /* start code */, 0x67, 66 /* profile_idc */, + 0 /* constraints */, 30 /* level_idc */ + }; + static const u8 pps[] = { + 0, 0, 0, 1 /* start code */, 0x68 + }; + + unsigned bits = 0; + unsigned mbs_w = vop_hsize(vh); + unsigned mbs_h = vop_vsize(vh); + + write_bytes(out, &bits, sps, sizeof(sps)); + write_ue(out, &bits, 0); /* seq_parameter_set_id */ + write_ue(out, &bits, 5); /* log2_max_frame_num_minus4 */ + write_ue(out, &bits, 0); /* pic_order_cnt_type */ + write_ue(out, &bits, 6); /* log2_max_pic_order_cnt_lsb_minus4 */ + write_ue(out, &bits, 1); /* max_num_ref_frames */ + write_bits(out, &bits, 0, 1); /* gaps_in_frame_num_value_allowed_flag */ + write_ue(out, &bits, mbs_w - 1); /* pic_width_in_mbs_minus1 */ + write_ue(out, &bits, mbs_h - 1); /* pic_height_in_map_units_minus1 */ + write_bits(out, &bits, 1, 1); /* frame_mbs_only_flag */ + write_bits(out, &bits, 1, 1); /* direct_8x8_frame_field_flag */ + write_bits(out, &bits, 0, 1); /* frame_cropping_flag */ + write_bits(out, &bits, 0, 1); /* vui_parameters_present_flag */ + write_h264_end(out, &bits, 0); + + write_bytes(out, &bits, pps, sizeof(pps)); + write_ue(out, &bits, 0); /* pic_parameter_set_id */ + write_ue(out, &bits, 0); /* seq_parameter_set_id */ + write_bits(out, &bits, 0, 1); /* entropy_coding_mode_flag */ + write_bits(out, &bits, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */ + write_ue(out, &bits, 0); /* num_slice_groups_minus1 */ + write_ue(out, &bits, 0); /* num_ref_idx_l0_default_active_minus1 */ + write_ue(out, &bits, 0); /* num_ref_idx_l1_default_active_minus1 */ + write_bits(out, &bits, 0, 1); /* weighted_pred_flag */ + write_bits(out, &bits, 0, 2); /* weighted_bipred_idc */ + write_se(out, &bits, 0); /* pic_init_qp_minus26 */ + write_se(out, &bits, 0); /* pic_init_qs_minus26 */ + write_se(out, &bits, 2); /* chroma_qp_index_offset */ + write_bits(out, &bits, 0, 1); /* deblocking_filter_control_present_flag */ + write_bits(out, &bits, 1, 1); /* constrained_intra_pred_flag */ + write_bits(out, &bits, 0, 1); /* redundant_pic_cnt_present_flag */ + write_h264_end(out, &bits, 1); +} + +static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, + struct videobuf_buffer *vb, + struct videobuf_dmabuf *vbuf) +{ + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + +#define VH_WORDS 16 +#define MAX_VOL_HEADER_LENGTH 64 + + __le32 vh[VH_WORDS]; + int ret; + int frame_size, frame_off; + int skip = 0; + + if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh))) + return -EINVAL; + + /* First get the hardware vop header (not real mpeg) */ + ret = enc_get_mpeg_dma(solo_dev, vh, enc_buf->off, sizeof(vh)); + if (WARN_ON_ONCE(ret)) + return ret; + + if (WARN_ON_ONCE(vop_size(vh) > enc_buf->size)) + return -EINVAL; + + vb->width = vop_hsize(vh) << 4; + vb->height = vop_vsize(vh) << 4; + vb->size = vop_size(vh); + + /* If this is a key frame, add extra m4v header */ + if (!enc_buf->vop) { + u8 header[MAX_VOL_HEADER_LENGTH], *out = header; + + if (solo_dev->flags & FLAGS_6110) + h264_write_vol(&out, solo_dev, vh); + else + mpeg4_write_vol(&out, solo_dev, vh, + solo_dev->fps * 1000, + solo_enc->interval * 1000); + skip = out - header; + enc_write_sg(vbuf->sglist, header, skip); + /* Adjust the dma buffer past this header */ + vb->size += skip; + } + + /* Now get the actual mpeg payload */ + frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); + frame_size = enc_buf->size - sizeof(vh); + + ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, + skip, frame_off, frame_size); + WARN_ON_ONCE(ret); + + return ret; +} + +static void solo_enc_fillbuf(struct solo_enc_fh *fh, + struct videobuf_buffer *vb) +{ + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + struct solo_enc_buf *enc_buf = NULL; + struct videobuf_dmabuf *vbuf; + int ret; + int error = 1; + u16 idx = fh->rd_idx; + + while (idx != solo_dev->enc_wr_idx) { + struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx]; + + idx = (idx + 1) % SOLO_NR_RING_BUFS; + + if (ebuf->ch != solo_enc->ch) + continue; + + if (fh->fmt == V4L2_PIX_FMT_MPEG) { + if (fh->type == ebuf->type) { + enc_buf = ebuf; + break; + } + } else { + /* For mjpeg, keep reading to the newest frame */ + enc_buf = ebuf; + } + } + + fh->rd_idx = idx; + + if (WARN_ON_ONCE(!enc_buf)) + goto buf_err; + + if ((fh->fmt == V4L2_PIX_FMT_MPEG && + vb->bsize < enc_buf->size) || + (fh->fmt == V4L2_PIX_FMT_MJPEG && + vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) { + WARN_ON_ONCE(1); + goto buf_err; + } + + vbuf = videobuf_to_dma(vb); + if (WARN_ON_ONCE(!vbuf)) + goto buf_err; + + if (fh->fmt == V4L2_PIX_FMT_MPEG) + ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf); + else + ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf); + + if (!ret) + error = 0; + +buf_err: + if (error) { + vb->state = VIDEOBUF_ERROR; + } else { + vb->field_count++; + vb->ts = enc_buf->ts; + vb->state = VIDEOBUF_DONE; + } + + wake_up(&vb->done); + + return; +} + +static void solo_enc_thread_try(struct solo_enc_fh *fh) +{ + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + struct videobuf_buffer *vb; + + for (;;) { + spin_lock(&solo_enc->lock); + + if (fh->rd_idx == solo_dev->enc_wr_idx) + break; + + if (list_empty(&fh->vidq_active)) + break; + + vb = list_first_entry(&fh->vidq_active, + struct videobuf_buffer, queue); + + if (!waitqueue_active(&vb->done)) + break; + + list_del(&vb->queue); + + spin_unlock(&solo_enc->lock); + + solo_enc_fillbuf(fh, vb); + } + + assert_spin_locked(&solo_enc->lock); + spin_unlock(&solo_enc->lock); +} + +static int solo_enc_thread(void *data) +{ + struct solo_enc_fh *fh = data; + struct solo_enc_dev *solo_enc = fh->enc; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_enc->thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_enc_thread_try(fh); + try_to_freeze(); + } + + remove_wait_queue(&solo_enc->thread_wait, &wait); + + return 0; +} + +void solo_motion_isr(struct solo6010_dev *solo_dev) +{ + u32 status; + int i; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_MOTION); + + status = solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS); + + for (i = 0; i < solo_dev->nr_chans; i++) { + struct solo_enc_dev *solo_enc = solo_dev->v4l2_enc[i]; + + BUG_ON(solo_enc == NULL); + + if (solo_enc->motion_detected) + continue; + if (!(status & (1 << i))) + continue; + + solo_enc->motion_detected = 1; + } +} + +void solo_enc_v4l2_isr(struct solo6010_dev *solo_dev) +{ + struct solo_enc_buf *enc_buf; + u32 mpeg_current, mpeg_next, mpeg_size; + u32 jpeg_current, jpeg_next, jpeg_size; + u32 reg_mpeg_size; + u8 cur_q, vop_type; + u8 ch; + enum solo_enc_types enc_type; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER); + + cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS; + + reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7; + + while (solo_dev->enc_idx != cur_q) { + mpeg_current = solo_reg_read(solo_dev, + SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); + jpeg_current = solo_reg_read(solo_dev, + SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); + solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; + mpeg_next = solo_reg_read(solo_dev, + SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); + jpeg_next = solo_reg_read(solo_dev, + SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); + + ch = (mpeg_current >> 24) & 0x1f; + if (ch >= SOLO_MAX_CHANNELS) { + ch -= SOLO_MAX_CHANNELS; + enc_type = SOLO_ENC_TYPE_EXT; + } else + enc_type = SOLO_ENC_TYPE_STD; + + vop_type = (mpeg_current >> 29) & 3; + + mpeg_current &= 0x00ffffff; + mpeg_next &= 0x00ffffff; + jpeg_current &= 0x00ffffff; + jpeg_next &= 0x00ffffff; + + mpeg_size = (SOLO_MP4E_EXT_SIZE(solo_dev) + + mpeg_next - mpeg_current) % + SOLO_MP4E_EXT_SIZE(solo_dev); + + jpeg_size = (SOLO_JPEG_EXT_SIZE(solo_dev) + + jpeg_next - jpeg_current) % + SOLO_JPEG_EXT_SIZE(solo_dev); + + /* XXX I think this means we had a ring overflow? */ + if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) { + enc_reset_gop(solo_dev, ch); + continue; + } + + /* When resetting the GOP, skip frames until I-frame */ + if (enc_gop_reset(solo_dev, ch, vop_type)) + continue; + + enc_buf = &solo_dev->enc_buf[solo_dev->enc_wr_idx]; + + enc_buf->vop = vop_type; + enc_buf->ch = ch; + enc_buf->off = mpeg_current; + enc_buf->size = mpeg_size; + enc_buf->jpeg_off = jpeg_current; + enc_buf->jpeg_size = jpeg_size; + enc_buf->type = enc_type; + + do_gettimeofday(&enc_buf->ts); + + solo_dev->enc_wr_idx = (solo_dev->enc_wr_idx + 1) % + SOLO_NR_RING_BUFS; + + wake_up_interruptible(&solo_dev->v4l2_enc[ch]->thread_wait); + } + + return; +} + +static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + *size = FRAME_BUF_SIZE; + + if (*count < MIN_VID_BUFFERS) + *count = MIN_VID_BUFFERS; + + return 0; +} + +static int solo_enc_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct solo_enc_fh *fh = vq->priv_data; + struct solo_enc_dev *solo_enc = fh->enc; + + vb->size = FRAME_BUF_SIZE; + if (vb->baddr != 0 && vb->bsize < vb->size) + return -EINVAL; + + /* These properties only change when queue is idle */ + vb->width = solo_enc->width; + vb->height = solo_enc->height; + vb->field = field; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + int rc = videobuf_iolock(vq, vb, NULL); + if (rc < 0) { + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); + vb->state = VIDEOBUF_NEEDS_INIT; + return rc; + } + } + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +static void solo_enc_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct solo_enc_fh *fh = vq->priv_data; + + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &fh->vidq_active); + wake_up_interruptible(&fh->enc->thread_wait); +} + +static void solo_enc_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops solo_enc_video_qops = { + .buf_setup = solo_enc_buf_setup, + .buf_prepare = solo_enc_buf_prepare, + .buf_queue = solo_enc_buf_queue, + .buf_release = solo_enc_buf_release, +}; + +static unsigned int solo_enc_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct solo_enc_fh *fh = file->private_data; + + return videobuf_poll_stream(file, &fh->vidq, wait); +} + +static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct solo_enc_fh *fh = file->private_data; + + return videobuf_mmap_mapper(&fh->vidq, vma); +} + +static int solo_enc_open(struct file *file) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_enc_fh *fh; + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (fh == NULL) + return -ENOMEM; + + fh->enc = solo_enc; + file->private_data = fh; + INIT_LIST_HEAD(&fh->vidq_active); + fh->fmt = V4L2_PIX_FMT_MPEG; + fh->type = SOLO_ENC_TYPE_STD; + + videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops, + &solo_enc->solo_dev->pdev->dev, + &solo_enc->lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct videobuf_buffer), fh, NULL); + + return 0; +} + +static ssize_t solo_enc_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct solo_enc_fh *fh = file->private_data; + struct solo_enc_dev *solo_enc = fh->enc; + + /* Make sure the encoder is on */ + if (!fh->enc_on) { + int ret; + + spin_lock(&solo_enc->lock); + ret = solo_enc_on(fh); + spin_unlock(&solo_enc->lock); + if (ret) + return ret; + + ret = solo_start_fh_thread(fh); + if (ret) + return ret; + } + + return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static int solo_enc_release(struct file *file) +{ + struct solo_enc_fh *fh = file->private_data; + struct solo_enc_dev *solo_enc = fh->enc; + + videobuf_stop(&fh->vidq); + videobuf_mmap_free(&fh->vidq); + + spin_lock(&solo_enc->lock); + solo_enc_off(fh); + spin_unlock(&solo_enc->lock); + + kfree(fh); + + return 0; +} + +static int solo_enc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + + strcpy(cap->driver, SOLO6010_NAME); + snprintf(cap->card, sizeof(cap->card), "Softlogic 6010 Enc %d", + solo_enc->ch); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", + pci_name(solo_dev->pdev)); + cap->version = SOLO6010_VER_NUM; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; +} + +static int solo_enc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + + if (input->index) + return -EINVAL; + + snprintf(input->name, sizeof(input->name), "Encoder %d", + solo_enc->ch + 1); + input->type = V4L2_INPUT_TYPE_CAMERA; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + input->std = V4L2_STD_NTSC_M; + else + input->std = V4L2_STD_PAL_B; + + if (!tw28_get_video_status(solo_dev, solo_enc->ch)) + input->status = V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int solo_enc_set_input(struct file *file, void *priv, unsigned int index) +{ + if (index) + return -EINVAL; + + return 0; +} + +static int solo_enc_get_input(struct file *file, void *priv, + unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int solo_enc_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_MPEG; + strcpy(f->description, "MPEG-4 AVC"); + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_MJPEG; + strcpy(f->description, "MJPEG"); + break; + default: + return -EINVAL; + } + + f->flags = V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static int solo_enc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->pixelformat != V4L2_PIX_FMT_MPEG && + pix->pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + /* We cannot change width/height in mid read */ + if (atomic_read(&solo_enc->readers) > 0) { + if (pix->width != solo_enc->width || + pix->height != solo_enc->height) + return -EBUSY; + } + + if (pix->width < solo_dev->video_hsize || + pix->height < solo_dev->video_vsize << 1) { + /* Default to CIF 1/2 size */ + pix->width = solo_dev->video_hsize >> 1; + pix->height = solo_dev->video_vsize; + } else { + /* Full frame */ + pix->width = solo_dev->video_hsize; + pix->height = solo_dev->video_vsize << 1; + } + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_INTERLACED; + else if (pix->field != V4L2_FIELD_INTERLACED) + pix->field = V4L2_FIELD_INTERLACED; + + /* Just set these */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->sizeimage = FRAME_BUF_SIZE; + + return 0; +} + +static int solo_enc_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + spin_lock(&solo_enc->lock); + + ret = solo_enc_try_fmt_cap(file, priv, f); + if (ret) { + spin_unlock(&solo_enc->lock); + return ret; + } + + if (pix->width == solo_dev->video_hsize) + solo_enc->mode = SOLO_ENC_MODE_D1; + else + solo_enc->mode = SOLO_ENC_MODE_CIF; + + /* This does not change the encoder at all */ + fh->fmt = pix->pixelformat; + + if (pix->priv) + fh->type = SOLO_ENC_TYPE_EXT; + ret = solo_enc_on(fh); + + spin_unlock(&solo_enc->lock); + + if (ret) + return ret; + + return solo_start_fh_thread(fh); +} + +static int solo_enc_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_enc->width; + pix->height = solo_enc->height; + pix->pixelformat = fh->fmt; + pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : + V4L2_FIELD_NONE; + pix->sizeimage = FRAME_BUF_SIZE; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int solo_enc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req) +{ + struct solo_enc_fh *fh = priv; + + return videobuf_reqbufs(&fh->vidq, req); +} + +static int solo_enc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct solo_enc_fh *fh = priv; + + return videobuf_querybuf(&fh->vidq, buf); +} + +static int solo_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct solo_enc_fh *fh = priv; + + return videobuf_qbuf(&fh->vidq, buf); +} + +static int solo_enc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + int ret; + + /* Make sure the encoder is on */ + if (!fh->enc_on) { + spin_lock(&solo_enc->lock); + ret = solo_enc_on(fh); + spin_unlock(&solo_enc->lock); + if (ret) + return ret; + + ret = solo_start_fh_thread(fh); + if (ret) + return ret; + } + + ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); + if (ret) + return ret; + + /* Signal motion detection */ + if (solo_is_motion_on(solo_enc)) { + buf->flags |= V4L2_BUF_FLAG_MOTION_ON; + if (solo_enc->motion_detected) { + buf->flags |= V4L2_BUF_FLAG_MOTION_DETECTED; + solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, + 1 << solo_enc->ch); + solo_enc->motion_detected = 0; + } + } + + /* Check for key frame on mpeg data */ + if (fh->fmt == V4L2_PIX_FMT_MPEG) { + struct videobuf_dmabuf *vbuf = + videobuf_to_dma(fh->vidq.bufs[buf->index]); + + if (vbuf) { + u8 *p = sg_virt(vbuf->sglist); + if (p[3] == 0x00) + buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + buf->flags |= V4L2_BUF_FLAG_PFRAME; + } + } + + return 0; +} + +static int solo_enc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct solo_enc_fh *fh = priv; + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamon(&fh->vidq); +} + +static int solo_enc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct solo_enc_fh *fh = priv; + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamoff(&fh->vidq); +} + +static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i) +{ + return 0; +} + +static int solo_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct solo_enc_fh *fh = priv; + struct solo6010_dev *solo_dev = fh->enc->solo_dev; + + if (fsize->pixel_format != V4L2_PIX_FMT_MPEG) + return -EINVAL; + + switch (fsize->index) { + case 0: + fsize->discrete.width = solo_dev->video_hsize >> 1; + fsize->discrete.height = solo_dev->video_vsize; + break; + case 1: + fsize->discrete.width = solo_dev->video_hsize; + fsize->discrete.height = solo_dev->video_vsize << 1; + break; + default: + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + return 0; +} + +static int solo_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fintv) +{ + struct solo_enc_fh *fh = priv; + struct solo6010_dev *solo_dev = fh->enc->solo_dev; + + if (fintv->pixel_format != V4L2_PIX_FMT_MPEG || fintv->index) + return -EINVAL; + + fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; + + fintv->stepwise.min.numerator = solo_dev->fps; + fintv->stepwise.min.denominator = 1; + + fintv->stepwise.max.numerator = solo_dev->fps; + fintv->stepwise.max.denominator = 15; + + fintv->stepwise.step.numerator = 1; + fintv->stepwise.step.denominator = 1; + + return 0; +} + +static int solo_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_captureparm *cp = &sp->parm.capture; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = solo_enc->interval; + cp->timeperframe.denominator = solo_dev->fps; + cp->capturemode = 0; + /* XXX: Shouldn't we be able to get/set this from videobuf? */ + cp->readbuffers = 2; + + return 0; +} + +static int solo_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_captureparm *cp = &sp->parm.capture; + + spin_lock(&solo_enc->lock); + + if (atomic_read(&solo_enc->readers) > 0) { + spin_unlock(&solo_enc->lock); + return -EBUSY; + } + + if ((cp->timeperframe.numerator == 0) || + (cp->timeperframe.denominator == 0)) { + /* reset framerate */ + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = solo_dev->fps; + } + + if (cp->timeperframe.denominator != solo_dev->fps) + cp->timeperframe.denominator = solo_dev->fps; + + if (cp->timeperframe.numerator > 15) + cp->timeperframe.numerator = 15; + + solo_enc->interval = cp->timeperframe.numerator; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + + solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1); + solo_update_mode(solo_enc); + + spin_unlock(&solo_enc->lock); + + return 0; +} + +static int solo_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + + qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); + if (!qc->id) + return -EINVAL; + + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0x00, 0xff, 1, 0x80); + case V4L2_CID_SHARPNESS: + return v4l2_ctrl_query_fill(qc, 0x00, 0x0f, 1, 0x00); + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(qc, 1, 255, 1, solo_dev->fps); +#ifdef PRIVATE_CIDS + case V4L2_CID_MOTION_THRESHOLD: + qc->flags |= V4L2_CTRL_FLAG_SLIDER; + qc->type = V4L2_CTRL_TYPE_INTEGER; + qc->minimum = 0; + qc->maximum = 0xffff; + qc->step = 1; + qc->default_value = SOLO_DEF_MOT_THRESH; + strlcpy(qc->name, "Motion Detection Threshold", + sizeof(qc->name)); + return 0; + case V4L2_CID_MOTION_ENABLE: + qc->type = V4L2_CTRL_TYPE_BOOLEAN; + qc->minimum = 0; + qc->maximum = qc->step = 1; + qc->default_value = 0; + strlcpy(qc->name, "Motion Detection Enable", sizeof(qc->name)); + return 0; +#else + case V4L2_CID_MOTION_THRESHOLD: + return v4l2_ctrl_query_fill(qc, 0, 0xffff, 1, + SOLO_DEF_MOT_THRESH); + case V4L2_CID_MOTION_ENABLE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); +#endif + case V4L2_CID_RDS_TX_RADIO_TEXT: + qc->type = V4L2_CTRL_TYPE_STRING; + qc->minimum = 0; + qc->maximum = OSD_TEXT_MAX; + qc->step = 1; + qc->default_value = 0; + strlcpy(qc->name, "OSD Text", sizeof(qc->name)); + return 0; + } + + return -EINVAL; +} + +static int solo_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + int err; + + qctrl.id = qmenu->id; + err = solo_queryctrl(file, priv, &qctrl); + if (err) + return err; + + return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); +} + +static int solo_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_SHARPNESS: + return tw28_get_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, + &ctrl->value); + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrl->value = solo_enc->gop; + break; + case V4L2_CID_MOTION_THRESHOLD: + ctrl->value = solo_enc->motion_thresh; + break; + case V4L2_CID_MOTION_ENABLE: + ctrl->value = solo_is_motion_on(solo_enc); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int solo_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo6010_dev *solo_dev = solo_enc->solo_dev; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_SHARPNESS: + return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, + ctrl->value); + case V4L2_CID_MPEG_VIDEO_ENCODING: + if (ctrl->value != V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) + return -ERANGE; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + if (ctrl->value < 1 || ctrl->value > 255) + return -ERANGE; + solo_enc->gop = ctrl->value; + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch), + solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch), + solo_enc->gop); + break; + case V4L2_CID_MOTION_THRESHOLD: + /* TODO accept value on lower 16-bits and use high + * 16-bits to assign the value to a specific block */ + if (ctrl->value < 0 || ctrl->value > 0xffff) + return -ERANGE; + solo_enc->motion_thresh = ctrl->value; + solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->value); + break; + case V4L2_CID_MOTION_ENABLE: + solo_motion_toggle(solo_enc, ctrl->value); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int solo_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + int i; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = (ctrls->controls + i); + int err; + + switch (ctrl->id) { + case V4L2_CID_RDS_TX_RADIO_TEXT: + if (ctrl->size - 1 > OSD_TEXT_MAX) + err = -ERANGE; + else { + err = copy_from_user(solo_enc->osd_text, + ctrl->string, + OSD_TEXT_MAX); + solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; + if (!err) + err = solo_osd_print(solo_enc); + } + break; + default: + err = -EINVAL; + } + + if (err < 0) { + ctrls->error_idx = i; + return err; + } + } + + return 0; +} + +static int solo_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + int i; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = (ctrls->controls + i); + int err; + + switch (ctrl->id) { + case V4L2_CID_RDS_TX_RADIO_TEXT: + if (ctrl->size < OSD_TEXT_MAX) { + ctrl->size = OSD_TEXT_MAX; + err = -ENOSPC; + } else { + err = copy_to_user(ctrl->string, + solo_enc->osd_text, + OSD_TEXT_MAX); + } + break; + default: + err = -EINVAL; + } + + if (err < 0) { + ctrls->error_idx = i; + return err; + } + } + + return 0; +} + +static const struct v4l2_file_operations solo_enc_fops = { + .owner = THIS_MODULE, + .open = solo_enc_open, + .release = solo_enc_release, + .read = solo_enc_read, + .poll = solo_enc_poll, + .mmap = solo_enc_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { + .vidioc_querycap = solo_enc_querycap, + .vidioc_s_std = solo_enc_s_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enc_enum_input, + .vidioc_s_input = solo_enc_set_input, + .vidioc_g_input = solo_enc_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = solo_enc_reqbufs, + .vidioc_querybuf = solo_enc_querybuf, + .vidioc_qbuf = solo_enc_qbuf, + .vidioc_dqbuf = solo_enc_dqbuf, + .vidioc_streamon = solo_enc_streamon, + .vidioc_streamoff = solo_enc_streamoff, + /* Frame size and interval */ + .vidioc_enum_framesizes = solo_enum_framesizes, + .vidioc_enum_frameintervals = solo_enum_frameintervals, + /* Video capture parameters */ + .vidioc_s_parm = solo_s_parm, + .vidioc_g_parm = solo_g_parm, + /* Controls */ + .vidioc_queryctrl = solo_queryctrl, + .vidioc_querymenu = solo_querymenu, + .vidioc_g_ctrl = solo_g_ctrl, + .vidioc_s_ctrl = solo_s_ctrl, + .vidioc_g_ext_ctrls = solo_g_ext_ctrls, + .vidioc_s_ext_ctrls = solo_s_ext_ctrls, +}; + +static struct video_device solo_enc_template = { + .name = SOLO6010_NAME, + .fops = &solo_enc_fops, + .ioctl_ops = &solo_enc_ioctl_ops, + .minor = -1, + .release = video_device_release, + + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, + .current_norm = V4L2_STD_NTSC_M, +}; + +static struct solo_enc_dev *solo_enc_alloc(struct solo6010_dev *solo_dev, u8 ch) +{ + struct solo_enc_dev *solo_enc; + int ret; + + solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); + if (!solo_enc) + return ERR_PTR(-ENOMEM); + + solo_enc->vfd = video_device_alloc(); + if (!solo_enc->vfd) { + kfree(solo_enc); + return ERR_PTR(-ENOMEM); + } + + solo_enc->solo_dev = solo_dev; + solo_enc->ch = ch; + + *solo_enc->vfd = solo_enc_template; + solo_enc->vfd->parent = &solo_dev->pdev->dev; + ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, + video_nr); + if (ret < 0) { + video_device_release(solo_enc->vfd); + kfree(solo_enc); + return ERR_PTR(ret); + } + + video_set_drvdata(solo_enc->vfd, solo_enc); + + snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), + "%s-enc (%i/%i)", SOLO6010_NAME, solo_dev->vfd->num, + solo_enc->vfd->num); + + if (video_nr != -1) + video_nr++; + + spin_lock_init(&solo_enc->lock); + init_waitqueue_head(&solo_enc->thread_wait); + atomic_set(&solo_enc->readers, 0); + + solo_enc->qp = SOLO_DEFAULT_QP; + solo_enc->gop = solo_dev->fps; + solo_enc->interval = 1; + solo_enc->mode = SOLO_ENC_MODE_CIF; + solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; + + spin_lock(&solo_enc->lock); + solo_update_mode(solo_enc); + spin_unlock(&solo_enc->lock); + + return solo_enc; +} + +static void solo_enc_free(struct solo_enc_dev *solo_enc) +{ + if (solo_enc == NULL) + return; + + video_unregister_device(solo_enc->vfd); + kfree(solo_enc); +} + +int solo_enc_v4l2_init(struct solo6010_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i); + if (IS_ERR(solo_dev->v4l2_enc[i])) + break; + } + + if (i != solo_dev->nr_chans) { + int ret = PTR_ERR(solo_dev->v4l2_enc[i]); + while (i--) + solo_enc_free(solo_dev->v4l2_enc[i]); + return ret; + } + + /* D1@MAX-FPS * 4 */ + solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; + + dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", + solo_dev->v4l2_enc[0]->vfd->num, + solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); + + return 0; +} + +void solo_enc_v4l2_exit(struct solo6010_dev *solo_dev) +{ + int i; + + solo6010_irq_off(solo_dev, SOLO_IRQ_MOTION); + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_enc_free(solo_dev->v4l2_enc[i]); +} diff --git a/drivers/staging/solo6x10/v4l2.c b/drivers/staging/solo6x10/v4l2.c new file mode 100644 index 0000000..e9d620a --- /dev/null +++ b/drivers/staging/solo6x10/v4l2.c @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "solo6x10.h" +#include "tw28.h" + +#define SOLO_HW_BPL 2048 +#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED + +/* Image size is two fields, SOLO_HW_BPL is one horizontal line */ +#define solo_vlines(__solo) (__solo->video_vsize * 2) +#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ + solo_vlines(__solo)) +#define solo_bytesperline(__solo) (__solo->video_hsize * 2) + +#define MIN_VID_BUFFERS 4 + +/* Simple file handle */ +struct solo_filehandle { + struct solo6010_dev *solo_dev; + struct videobuf_queue vidq; + struct task_struct *kthread; + spinlock_t slock; + int old_write; + struct list_head vidq_active; + struct p2m_desc desc[SOLO_NR_P2M_DESC]; + int desc_idx; +}; + +unsigned video_nr = -1; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); + +static void erase_on(struct solo6010_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + solo_dev->erasing = 1; + solo_dev->frame_blank = 0; +} + +static int erase_off(struct solo6010_dev *solo_dev) +{ + if (!solo_dev->erasing) + return 0; + + /* First time around, assert erase off */ + if (!solo_dev->frame_blank) + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); + /* Keep the erasing flag on for 8 frames minimum */ + if (solo_dev->frame_blank++ >= 8) + solo_dev->erasing = 0; + + return 1; +} + +void solo_video_in_isr(struct solo6010_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN); + wake_up_interruptible(&solo_dev->disp_thread_wait); +} + +static void solo_win_setup(struct solo6010_dev *solo_dev, u8 ch, + int sx, int sy, int ex, int ey, int scale) +{ + if (ch >= solo_dev->nr_chans) + return; + + /* Here, we just keep window/channel the same */ + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), + SOLO_VI_WIN_CHANNEL(ch) | + SOLO_VI_WIN_SX(sx) | + SOLO_VI_WIN_EX(ex) | + SOLO_VI_WIN_SCALE(scale)); + + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), + SOLO_VI_WIN_SY(sy) | + SOLO_VI_WIN_EY(ey)); +} + +static int solo_v4l2_ch_ext_4up(struct solo6010_dev *solo_dev, u8 idx, int on) +{ + u8 ch = idx * 4; + + if (ch >= solo_dev->nr_chans) + return -EINVAL; + + if (!on) { + u8 i; + for (i = ch; i < ch + 4; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + /* Row 1 */ + solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, 3); + solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, + solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); + /* Row 2 */ + solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, + solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); + solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, solo_dev->video_hsize, + solo_vlines(solo_dev), 3); + + return 0; +} + +static int solo_v4l2_ch_ext_16up(struct solo6010_dev *solo_dev, int on) +{ + int sy, ysize, hsize, i; + + if (!on) { + for (i = 0; i < 16; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + ysize = solo_vlines(solo_dev) / 4; + hsize = solo_dev->video_hsize / 4; + + for (sy = 0, i = 0; i < 4; i++, sy += ysize) { + solo_win_setup(solo_dev, i * 4, 0, sy, hsize, + sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, + hsize * 2, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, + hsize * 3, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, + solo_dev->video_hsize, sy + ysize, 5); + } + + return 0; +} + +static int solo_v4l2_ch(struct solo6010_dev *solo_dev, u8 ch, int on) +{ + u8 ext_ch; + + if (ch < solo_dev->nr_chans) { + solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, + on ? 0 : solo_vlines(solo_dev), + solo_dev->video_hsize, solo_vlines(solo_dev), + on ? 1 : 0); + return 0; + } + + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + ext_ch = ch - solo_dev->nr_chans; + + /* 4up's first */ + if (ext_ch < 4) + return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); + + /* Remaining case is 16up for 16-port */ + return solo_v4l2_ch_ext_16up(solo_dev, on); +} + +static int solo_v4l2_set_ch(struct solo6010_dev *solo_dev, u8 ch) +{ + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + erase_on(solo_dev); + + solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); + solo_v4l2_ch(solo_dev, ch, 1); + + solo_dev->cur_disp_ch = ch; + + return 0; +} + +static void disp_reset_desc(struct solo_filehandle *fh) +{ + /* We use desc mode, which ignores desc 0 */ + memset(fh->desc, 0, sizeof(*fh->desc)); + fh->desc_idx = 1; +} + +static int disp_flush_descs(struct solo_filehandle *fh) +{ + int ret; + + if (!fh->desc_idx) + return 0; + + ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP, + fh->desc, fh->desc_idx); + disp_reset_desc(fh); + + return ret; +} + +static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr, + u32 ext_addr, int size, int repeat, int ext_size) +{ + if (fh->desc_idx >= SOLO_NR_P2M_DESC) { + int ret = disp_flush_descs(fh); + if (ret) + return ret; + } + + solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr, + size, repeat, ext_size); + fh->desc_idx++; + + return 0; +} + +static void solo_fillbuf(struct solo_filehandle *fh, + struct videobuf_buffer *vb) +{ + struct solo6010_dev *solo_dev = fh->solo_dev; + struct videobuf_dmabuf *vbuf; + unsigned int fdma_addr; + int error = 1; + int i; + struct scatterlist *sg; + dma_addr_t sg_dma; + int sg_size_left; + + vbuf = videobuf_to_dma(vb); + if (!vbuf) + goto finish_buf; + + if (erase_off(solo_dev)) { + int i; + + /* Just blit to the entire sg list, ignoring size */ + for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) { + void *p = sg_virt(sg); + size_t len = sg_dma_len(sg); + + for (i = 0; i < len; i += 2) { + ((u8 *)p)[i] = 0x80; + ((u8 *)p)[i + 1] = 0x00; + } + } + + error = 0; + goto finish_buf; + } + + disp_reset_desc(fh); + sg = vbuf->sglist; + sg_dma = sg_dma_address(sg); + sg_size_left = sg_dma_len(sg); + + fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write * + (SOLO_HW_BPL * solo_vlines(solo_dev))); + + for (i = 0; i < solo_vlines(solo_dev); i++) { + int line_len = solo_bytesperline(solo_dev); + int lines; + + if (!sg_size_left) { + sg = sg_next(sg); + if (sg == NULL) + goto finish_buf; + sg_dma = sg_dma_address(sg); + sg_size_left = sg_dma_len(sg); + } + + /* No room for an entire line, so chunk it up */ + if (sg_size_left < line_len) { + int this_addr = fdma_addr; + + while (line_len > 0) { + int this_write; + + if (!sg_size_left) { + sg = sg_next(sg); + if (sg == NULL) + goto finish_buf; + sg_dma = sg_dma_address(sg); + sg_size_left = sg_dma_len(sg); + } + + this_write = min(sg_size_left, line_len); + + if (disp_push_desc(fh, sg_dma, this_addr, + this_write, 0, 0)) + goto finish_buf; + + line_len -= this_write; + sg_size_left -= this_write; + sg_dma += this_write; + this_addr += this_write; + } + + fdma_addr += SOLO_HW_BPL; + continue; + } + + /* Shove as many lines into a repeating descriptor as possible */ + lines = min(sg_size_left / line_len, + solo_vlines(solo_dev) - i); + + if (disp_push_desc(fh, sg_dma, fdma_addr, line_len, + lines - 1, SOLO_HW_BPL)) + goto finish_buf; + + i += lines - 1; + fdma_addr += SOLO_HW_BPL * lines; + sg_dma += lines * line_len; + sg_size_left -= lines * line_len; + } + + error = disp_flush_descs(fh); + +finish_buf: + if (error) { + vb->state = VIDEOBUF_ERROR; + } else { + vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev); + vb->state = VIDEOBUF_DONE; + vb->field_count++; + do_gettimeofday(&vb->ts); + } + + wake_up(&vb->done); + + return; +} + +static void solo_thread_try(struct solo_filehandle *fh) +{ + struct videobuf_buffer *vb; + unsigned int cur_write; + + for (;;) { + spin_lock(&fh->slock); + + if (list_empty(&fh->vidq_active)) + break; + + vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer, + queue); + + if (!waitqueue_active(&vb->done)) + break; + + cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev, + SOLO_VI_STATUS0)); + if (cur_write == fh->old_write) + break; + + fh->old_write = cur_write; + list_del(&vb->queue); + + spin_unlock(&fh->slock); + + solo_fillbuf(fh, vb); + } + + assert_spin_locked(&fh->slock); + spin_unlock(&fh->slock); +} + +static int solo_thread(void *data) +{ + struct solo_filehandle *fh = data; + struct solo6010_dev *solo_dev = fh->solo_dev; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_dev->disp_thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_thread_try(fh); + try_to_freeze(); + } + + remove_wait_queue(&solo_dev->disp_thread_wait, &wait); + + return 0; +} + +static int solo_start_thread(struct solo_filehandle *fh) +{ + fh->kthread = kthread_run(solo_thread, fh, SOLO6010_NAME "_disp"); + + if (IS_ERR(fh->kthread)) + return PTR_ERR(fh->kthread); + + return 0; +} + +static void solo_stop_thread(struct solo_filehandle *fh) +{ + if (fh->kthread) { + kthread_stop(fh->kthread); + fh->kthread = NULL; + } +} + +static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct solo_filehandle *fh = vq->priv_data; + struct solo6010_dev *solo_dev = fh->solo_dev; + + *size = solo_image_size(solo_dev); + + if (*count < MIN_VID_BUFFERS) + *count = MIN_VID_BUFFERS; + + return 0; +} + +static int solo_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct solo_filehandle *fh = vq->priv_data; + struct solo6010_dev *solo_dev = fh->solo_dev; + + vb->size = solo_image_size(solo_dev); + if (vb->baddr != 0 && vb->bsize < vb->size) + return -EINVAL; + + /* XXX: These properties only change when queue is idle */ + vb->width = solo_dev->video_hsize; + vb->height = solo_vlines(solo_dev); + vb->bytesperline = solo_bytesperline(solo_dev); + vb->field = field; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + int rc = videobuf_iolock(vq, vb, NULL); + if (rc < 0) { + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); + vb->state = VIDEOBUF_NEEDS_INIT; + return rc; + } + } + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +static void solo_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct solo_filehandle *fh = vq->priv_data; + struct solo6010_dev *solo_dev = fh->solo_dev; + + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &fh->vidq_active); + wake_up_interruptible(&solo_dev->disp_thread_wait); +} + +static void solo_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops solo_video_qops = { + .buf_setup = solo_buf_setup, + .buf_prepare = solo_buf_prepare, + .buf_queue = solo_buf_queue, + .buf_release = solo_buf_release, +}; + +static unsigned int solo_v4l2_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct solo_filehandle *fh = file->private_data; + + return videobuf_poll_stream(file, &fh->vidq, wait); +} + +static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct solo_filehandle *fh = file->private_data; + + return videobuf_mmap_mapper(&fh->vidq, vma); +} + +static int solo_v4l2_open(struct file *file) +{ + struct solo6010_dev *solo_dev = video_drvdata(file); + struct solo_filehandle *fh; + int ret; + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (fh == NULL) + return -ENOMEM; + + spin_lock_init(&fh->slock); + INIT_LIST_HEAD(&fh->vidq_active); + fh->solo_dev = solo_dev; + file->private_data = fh; + + ret = solo_start_thread(fh); + if (ret) { + kfree(fh); + return ret; + } + + videobuf_queue_sg_init(&fh->vidq, &solo_video_qops, + &solo_dev->pdev->dev, &fh->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + SOLO_DISP_PIX_FIELD, + sizeof(struct videobuf_buffer), fh, NULL); + + return 0; +} + +static ssize_t solo_v4l2_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct solo_filehandle *fh = file->private_data; + + return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static int solo_v4l2_release(struct file *file) +{ + struct solo_filehandle *fh = file->private_data; + + videobuf_stop(&fh->vidq); + videobuf_mmap_free(&fh->vidq); + solo_stop_thread(fh); + kfree(fh); + + return 0; +} + +static int solo_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_filehandle *fh = priv; + struct solo6010_dev *solo_dev = fh->solo_dev; + + strcpy(cap->driver, SOLO6010_NAME); + strcpy(cap->card, "Softlogic 6010"); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", + pci_name(solo_dev->pdev)); + cap->version = SOLO6010_VER_NUM; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; +} + +static int solo_enum_ext_input(struct solo6010_dev *solo_dev, + struct v4l2_input *input) +{ + static const char *dispnames_1[] = { "4UP" }; + static const char *dispnames_2[] = { "4UP-1", "4UP-2" }; + static const char *dispnames_5[] = { + "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" + }; + const char **dispnames; + + if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) + return -EINVAL; + + if (solo_dev->nr_ext == 5) + dispnames = dispnames_5; + else if (solo_dev->nr_ext == 2) + dispnames = dispnames_2; + else + dispnames = dispnames_1; + + snprintf(input->name, sizeof(input->name), "Multi %s", + dispnames[input->index - solo_dev->nr_chans]); + + return 0; +} + +static int solo_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_filehandle *fh = priv; + struct solo6010_dev *solo_dev = fh->solo_dev; + + if (input->index >= solo_dev->nr_chans) { + int ret = solo_enum_ext_input(solo_dev, input); + if (ret < 0) + return ret; + } else { + snprintf(input->name, sizeof(input->name), "Camera %d", + input->index + 1); + + /* We can only check this for normal inputs */ + if (!tw28_get_video_status(solo_dev, input->index)) + input->status = V4L2_IN_ST_NO_SIGNAL; + } + + input->type = V4L2_INPUT_TYPE_CAMERA; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + input->std = V4L2_STD_NTSC_M; + else + input->std = V4L2_STD_PAL_B; + + return 0; +} + +static int solo_set_input(struct file *file, void *priv, unsigned int index) +{ + struct solo_filehandle *fh = priv; + + return solo_v4l2_set_ch(fh->solo_dev, index); +} + +static int solo_get_input(struct file *file, void *priv, unsigned int *index) +{ + struct solo_filehandle *fh = priv; + + *index = fh->solo_dev->cur_disp_ch; + + return 0; +} + +static int solo_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_UYVY; + strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); + + return 0; +} + +static int solo_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_filehandle *fh = priv; + struct solo6010_dev *solo_dev = fh->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + int image_size = solo_image_size(solo_dev); + + /* Check supported sizes */ + if (pix->width != solo_dev->video_hsize) + pix->width = solo_dev->video_hsize; + if (pix->height != solo_vlines(solo_dev)) + pix->height = solo_vlines(solo_dev); + if (pix->sizeimage != image_size) + pix->sizeimage = image_size; + + /* Check formats */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = SOLO_DISP_PIX_FIELD; + + if (pix->pixelformat != V4L2_PIX_FMT_UYVY || + pix->field != SOLO_DISP_PIX_FIELD || + pix->colorspace != V4L2_COLORSPACE_SMPTE170M) + return -EINVAL; + + return 0; +} + +static int solo_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_filehandle *fh = priv; + + if (videobuf_queue_is_busy(&fh->vidq)) + return -EBUSY; + + /* For right now, if it doesn't match our running config, + * then fail */ + return solo_try_fmt_cap(file, priv, f); +} + +static int solo_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_filehandle *fh = priv; + struct solo6010_dev *solo_dev = fh->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_dev->video_hsize; + pix->height = solo_vlines(solo_dev); + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->field = SOLO_DISP_PIX_FIELD; + pix->sizeimage = solo_image_size(solo_dev); + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->bytesperline = solo_bytesperline(solo_dev); + + return 0; +} + +static int solo_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req) +{ + struct solo_filehandle *fh = priv; + + return videobuf_reqbufs(&fh->vidq, req); +} + +static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct solo_filehandle *fh = priv; + + return videobuf_querybuf(&fh->vidq, buf); +} + +static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct solo_filehandle *fh = priv; + + return videobuf_qbuf(&fh->vidq, buf); +} + +static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct solo_filehandle *fh = priv; + + return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); +} + +static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct solo_filehandle *fh = priv; + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamon(&fh->vidq); +} + +static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct solo_filehandle *fh = priv; + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamoff(&fh->vidq); +} + +static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i) +{ + return 0; +} + +static const u32 solo_motion_ctrls[] = { + V4L2_CID_MOTION_TRACE, + 0 +}; + +static const u32 *solo_ctrl_classes[] = { + solo_motion_ctrls, + NULL +}; + +static int solo_disp_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); + if (!qc->id) + return -EINVAL; + + switch (qc->id) { +#ifdef PRIVATE_CIDS + case V4L2_CID_MOTION_TRACE: + qc->type = V4L2_CTRL_TYPE_BOOLEAN; + qc->minimum = 0; + qc->maximum = qc->step = 1; + qc->default_value = 0; + strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name)); + return 0; +#else + case V4L2_CID_MOTION_TRACE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); +#endif + } + return -EINVAL; +} + +static int solo_disp_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct solo_filehandle *fh = priv; + struct solo6010_dev *solo_dev = fh->solo_dev; + + switch (ctrl->id) { + case V4L2_CID_MOTION_TRACE: + ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR) + ? 1 : 0; + return 0; + } + return -EINVAL; +} + +static int solo_disp_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct solo_filehandle *fh = priv; + struct solo6010_dev *solo_dev = fh->solo_dev; + + switch (ctrl->id) { + case V4L2_CID_MOTION_TRACE: + if (ctrl->value) { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, + SOLO_VI_MOTION_Y_ADD | + SOLO_VI_MOTION_Y_VALUE(0x20) | + SOLO_VI_MOTION_CB_VALUE(0x10) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, + SOLO_VI_MOTION_CR_ADD | + SOLO_VI_MOTION_Y_VALUE(0x10) | + SOLO_VI_MOTION_CB_VALUE(0x80) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + } else { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); + } + return 0; + } + return -EINVAL; +} + +static const struct v4l2_file_operations solo_v4l2_fops = { + .owner = THIS_MODULE, + .open = solo_v4l2_open, + .release = solo_v4l2_release, + .read = solo_v4l2_read, + .poll = solo_v4l2_poll, + .mmap = solo_v4l2_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { + .vidioc_querycap = solo_querycap, + .vidioc_s_std = solo_s_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enum_input, + .vidioc_s_input = solo_set_input, + .vidioc_g_input = solo_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = solo_reqbufs, + .vidioc_querybuf = solo_querybuf, + .vidioc_qbuf = solo_qbuf, + .vidioc_dqbuf = solo_dqbuf, + .vidioc_streamon = solo_streamon, + .vidioc_streamoff = solo_streamoff, + /* Controls */ + .vidioc_queryctrl = solo_disp_queryctrl, + .vidioc_g_ctrl = solo_disp_g_ctrl, + .vidioc_s_ctrl = solo_disp_s_ctrl, +}; + +static struct video_device solo_v4l2_template = { + .name = SOLO6010_NAME, + .fops = &solo_v4l2_fops, + .ioctl_ops = &solo_v4l2_ioctl_ops, + .minor = -1, + .release = video_device_release, + + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, + .current_norm = V4L2_STD_NTSC_M, +}; + +int solo_v4l2_init(struct solo6010_dev *solo_dev) +{ + int ret; + int i; + + init_waitqueue_head(&solo_dev->disp_thread_wait); + + solo_dev->vfd = video_device_alloc(); + if (!solo_dev->vfd) + return -ENOMEM; + + *solo_dev->vfd = solo_v4l2_template; + solo_dev->vfd->parent = &solo_dev->pdev->dev; + + ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr); + if (ret < 0) { + video_device_release(solo_dev->vfd); + solo_dev->vfd = NULL; + return ret; + } + + video_set_drvdata(solo_dev->vfd, solo_dev); + + snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", + SOLO6010_NAME, solo_dev->vfd->num); + + if (video_nr != -1) + video_nr++; + + dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " + "%d inputs (%d extended)\n", solo_dev->vfd->num, + solo_dev->nr_chans, solo_dev->nr_ext); + + /* Cycle all the channels and clear */ + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_v4l2_set_ch(solo_dev, i); + while (erase_off(solo_dev)) + ;/* Do nothing */ + } + + /* Set the default display channel */ + solo_v4l2_set_ch(solo_dev, 0); + while (erase_off(solo_dev)) + ;/* Do nothing */ + + solo6010_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); + + return 0; +} + +void solo_v4l2_exit(struct solo6010_dev *solo_dev) +{ + solo6010_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); + if (solo_dev->vfd) { + video_unregister_device(solo_dev->vfd); + solo_dev->vfd = NULL; + } +} -- cgit v0.10.2