Staging: solo6x10: New driver (staging) for Softlogic 6x10
This driver supports Softlogic 6x10 based codec cards Signed-off-by: Ben Collins <bcollins@bluecherry.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
412b2d08b9
commit
faa4fd2a09
|
@ -0,0 +1,7 @@
|
|||
config SOLO6X10
|
||||
tristate "Softlogic 6x10 MPEG codec cards"
|
||||
depends on PCI && VIDEO_DEV
|
||||
select VIDEOBUF_DMA_CONTIG
|
||||
---help---
|
||||
This driver supports the Softlogic based MPEG-4 and h.264 codec
|
||||
codec cards.
|
|
@ -0,0 +1,6 @@
|
|||
solo6x10-objs := 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
|
||||
|
||||
obj-$(CONFIG_SOLO6X10) := solo6x10.o
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "solo6010.h"
|
||||
#include "solo6010-tw28.h"
|
||||
|
||||
MODULE_DESCRIPTION("Softlogic 6010 MP4 Encoder/Decoder V4L2/ALSA Driver");
|
||||
MODULE_AUTHOR("Ben Collins <bcollins@bluecherry.net>");
|
||||
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;
|
||||
|
||||
if ((solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL)) == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
solo_dev->pdev = pdev;
|
||||
spin_lock_init(&solo_dev->reg_io_lock);
|
||||
pci_set_drvdata(pdev, solo_dev);
|
||||
|
||||
if ((ret = pci_enable_device(pdev)))
|
||||
goto fail_probe;
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
if ((ret = pci_request_regions(pdev, SOLO6010_NAME)))
|
||||
goto fail_probe;
|
||||
|
||||
if ((solo_dev->reg_base = pci_ioremap_bar(pdev, 0)) == 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;
|
||||
}
|
||||
|
||||
/* Disable all interrupts to start */
|
||||
solo6010_irq_off(solo_dev, ~0);
|
||||
|
||||
/* Initial global settings */
|
||||
solo_reg_write(solo_dev, SOLO_SYS_CFG, SOLO_SYS_CFG_SDRAM64BIT |
|
||||
SOLO_SYS_CFG_INPUTDIV(25) |
|
||||
SOLO_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) |
|
||||
SOLO_SYS_CFG_OUTDIV(3));
|
||||
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);
|
||||
|
||||
if ((ret = solo_i2c_init(solo_dev)))
|
||||
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));
|
||||
|
||||
if ((ret = solo_p2m_init(solo_dev)))
|
||||
goto fail_probe;
|
||||
|
||||
if ((ret = solo_disp_init(solo_dev)))
|
||||
goto fail_probe;
|
||||
|
||||
if ((ret = solo_gpio_init(solo_dev)))
|
||||
goto fail_probe;
|
||||
|
||||
if ((ret = solo_tw28_init(solo_dev)))
|
||||
goto fail_probe;
|
||||
|
||||
if ((ret = solo_v4l2_init(solo_dev)))
|
||||
goto fail_probe;
|
||||
|
||||
if ((ret = solo_enc_init(solo_dev)))
|
||||
goto fail_probe;
|
||||
|
||||
if ((ret = solo_enc_v4l2_init(solo_dev)))
|
||||
goto fail_probe;
|
||||
|
||||
if ((ret = solo_g723_init(solo_dev)))
|
||||
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[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)},
|
||||
{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_COMMSOLO_4)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_9)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_COMMSOLO_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);
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
|
||||
#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_dev)));
|
||||
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#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(solo_dev) >> 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(solo_dev) +
|
||||
(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_dev) +
|
||||
(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)
|
||||
{
|
||||
solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL,
|
||||
(2 << 24) | (2 << 16) | (2 << 8) | (2 << 0));
|
||||
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);
|
||||
}
|
||||
|
||||
static void solo_mp4e_config(struct solo6010_dev *solo_dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* 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_BYTE_ALIGN(2) |
|
||||
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);
|
||||
|
||||
solo_reg_write(solo_dev, SOLO_VE_ATTR,
|
||||
SOLO_VE_LITTLE_ENDIAN |
|
||||
SOLO_COMP_ATTR_FCODE(1) |
|
||||
SOLO_COMP_TIME_INC(0) |
|
||||
SOLO_COMP_TIME_WIDTH(15) |
|
||||
SOLO_DCT_INTERVAL(36 / 4));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/control.h>
|
||||
|
||||
#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 err;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if ((ret = solo_snd_pcm_init(solo_dev)) < 0)
|
||||
goto snd_error;
|
||||
|
||||
if ((ret = snd_card_register(card)) < 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);
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#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);
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 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 <linux/kernel.h>
|
||||
|
||||
#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?
|
||||
|
||||
down(&solo_dev->i2c_sem);
|
||||
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;
|
||||
|
||||
up(&solo_dev->i2c_sem);
|
||||
|
||||
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);
|
||||
init_MUTEX(&solo_dev->i2c_sem);
|
||||
|
||||
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;
|
||||
|
||||
if ((ret = i2c_add_adapter(adap))) {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 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 */
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 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(__solo) 0x00000000
|
||||
#define SOLO_DISP_EXT_SIZE 0x00480000
|
||||
|
||||
#define SOLO_DEC2LIVE_EXT_ADDR(__solo) \
|
||||
(SOLO_DISP_EXT_ADDR(__solo) + SOLO_DISP_EXT_SIZE)
|
||||
#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000
|
||||
|
||||
#define SOLO_OSG_EXT_ADDR(__solo) \
|
||||
(SOLO_DEC2LIVE_EXT_ADDR(__solo) + SOLO_DEC2LIVE_EXT_SIZE)
|
||||
#define SOLO_OSG_EXT_SIZE 0x00120000
|
||||
|
||||
#define SOLO_EOSD_EXT_ADDR(__solo) \
|
||||
(SOLO_OSG_EXT_ADDR(__solo) + SOLO_OSG_EXT_SIZE)
|
||||
#define SOLO_EOSD_EXT_SIZE 0x00010000
|
||||
|
||||
#define SOLO_MOTION_EXT_ADDR(__solo) \
|
||||
(SOLO_EOSD_EXT_ADDR(__solo) + \
|
||||
(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 */
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 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 */
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#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);
|
||||
WARN_ON(id >= SOLO_NR_P2M);
|
||||
if (!size || id >= SOLO_NR_P2M)
|
||||
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 solo_p2m_dev *p2m_dev;
|
||||
unsigned int timeout = 0;
|
||||
|
||||
WARN_ON(!size);
|
||||
WARN_ON(id >= SOLO_NR_P2M);
|
||||
if (!size || id >= SOLO_NR_P2M)
|
||||
return -EINVAL;
|
||||
|
||||
p2m_dev = &solo_dev->p2m_dev[id];
|
||||
|
||||
down(&p2m_dev->sem);
|
||||
|
||||
start_dma:
|
||||
INIT_COMPLETION(p2m_dev->completion);
|
||||
p2m_dev->error = 0;
|
||||
solo_reg_write(solo_dev, SOLO_P2M_TAR_ADR(id), dma_addr);
|
||||
solo_reg_write(solo_dev, SOLO_P2M_EXT_ADR(id), ext_addr);
|
||||
solo_reg_write(solo_dev, SOLO_P2M_EXT_CFG(id),
|
||||
SOLO_P2M_COPY_SIZE(size >> 2));
|
||||
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id),
|
||||
SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) |
|
||||
(wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON);
|
||||
|
||||
timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ);
|
||||
|
||||
solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0);
|
||||
|
||||
/* XXX Really looks to me like we will get stuck here if a
|
||||
* real PCI P2M error occurs */
|
||||
if (p2m_dev->error)
|
||||
goto start_dma;
|
||||
|
||||
up(&p2m_dev->sem);
|
||||
|
||||
return (timeout == 0) ? -EAGAIN : 0;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id));
|
||||
complete(&solo_dev->p2m_dev[id].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];
|
||||
|
||||
init_MUTEX(&p2m_dev->sem);
|
||||
init_completion(&p2m_dev->completion);
|
||||
|
||||
solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(i),
|
||||
__pa(p2m_dev->desc));
|
||||
|
||||
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(0) |
|
||||
SOLO_P2M_PCI_MASTER_MODE);
|
||||
solo6010_irq_on(solo_dev, SOLO_IRQ_P2M(i));
|
||||
}
|
||||
|
||||
run_p2m_test(solo_dev);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,657 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 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 SOLO_SYS_CFG_FOUT_EN 0x00000001
|
||||
#define SOLO_SYS_CFG_PLL_BYPASS 0x00000002
|
||||
#define SOLO_SYS_CFG_PLL_PWDN 0x00000004
|
||||
#define SOLO_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3)
|
||||
#define SOLO_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5)
|
||||
#define SOLO_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14)
|
||||
#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
|
||||
#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_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 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 SOLO_VE_BYTE_ALIGN(n) ((n)<<24)
|
||||
#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))
|
||||
struct videnc_status {
|
||||
union {
|
||||
u32 status0;
|
||||
struct {
|
||||
u32 mp4_enc_code_size:20, sad_motion:1, vid_motion:1,
|
||||
vop_type:2, video_channel:5, source_field_idx:1,
|
||||
interlace:1, progressive:1;
|
||||
} status0_st;
|
||||
};
|
||||
union {
|
||||
u32 status1;
|
||||
struct {
|
||||
u32 vsize:8, hsize:8, last_queue:4, foo1:8, scale:4;
|
||||
} status1_st;
|
||||
};
|
||||
union {
|
||||
u32 status4;
|
||||
struct {
|
||||
u32 jpeg_code_size:20, interval:10, foo1:2;
|
||||
} status4_st;
|
||||
};
|
||||
union {
|
||||
u32 status9;
|
||||
struct {
|
||||
u32 channel:5, foo1:27;
|
||||
} status9_st;
|
||||
};
|
||||
union {
|
||||
u32 status10;
|
||||
struct {
|
||||
u32 mp4_code_size:20, foo:12;
|
||||
} status10_st;
|
||||
};
|
||||
union {
|
||||
u32 status11;
|
||||
struct {
|
||||
u32 last_queue:8, foo1:24;
|
||||
} status11_st;
|
||||
};
|
||||
};
|
||||
|
||||
#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 SOLO_VE_CH_MOT(ch) (0x0740+((ch)*4))
|
||||
#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 SOLO_VD_CFG_NO_WRITE_NO_WINDOW (1<<24)
|
||||
#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 */
|
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
|
||||
#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 tw2864_setup(struct solo6010_dev *solo_dev, u8 dev_addr)
|
||||
{
|
||||
u8 tbl_tw2864_common[256];
|
||||
int i;
|
||||
|
||||
memcpy(tbl_tw2864_common, tbl_tw2864_template,
|
||||
sizeof(tbl_tw2864_common));
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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:
|
||||
printk("solo6010: 2865 support not enabled\n");
|
||||
return -EINVAL;
|
||||
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->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->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);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 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 */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,859 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/videobuf-dma-contig.h>
|
||||
|
||||
#include "solo6010.h"
|
||||
#include "solo6010-tw28.h"
|
||||
|
||||
#define SOLO_HW_BPL 2048
|
||||
#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED
|
||||
#define SOLO_DISP_BUF_SIZE (64 * 1024) // 64k
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
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 solo_fillbuf(struct solo_filehandle *fh,
|
||||
struct videobuf_buffer *vb)
|
||||
{
|
||||
struct solo6010_dev *solo_dev = fh->solo_dev;
|
||||
dma_addr_t vbuf;
|
||||
unsigned int fdma_addr;
|
||||
int frame_size;
|
||||
int error = 1;
|
||||
int i;
|
||||
|
||||
if (!(vbuf = videobuf_to_dma_contig(vb)))
|
||||
goto finish_buf;
|
||||
|
||||
if (erase_off(solo_dev)) {
|
||||
void *p = videobuf_queue_to_vaddr(&fh->vidq, vb);
|
||||
int image_size = solo_image_size(solo_dev);
|
||||
for (i = 0; i < image_size; i += 2) {
|
||||
((u8 *)p)[i] = 0x80;
|
||||
((u8 *)p)[i + 1] = 0x00;
|
||||
}
|
||||
error = 0;
|
||||
goto finish_buf;
|
||||
}
|
||||
|
||||
frame_size = SOLO_HW_BPL * solo_vlines(solo_dev);
|
||||
fdma_addr = SOLO_DISP_EXT_ADDR(solo_dev) + (fh->old_write * frame_size);
|
||||
|
||||
for (i = 0; i < frame_size / SOLO_DISP_BUF_SIZE; i++) {
|
||||
int j;
|
||||
for (j = 0; j < (SOLO_DISP_BUF_SIZE / SOLO_HW_BPL); j++) {
|
||||
if (solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_DISP, 0,
|
||||
vbuf, fdma_addr + (j * SOLO_HW_BPL),
|
||||
solo_bytesperline(solo_dev)))
|
||||
goto finish_buf;
|
||||
vbuf += solo_bytesperline(solo_dev);
|
||||
}
|
||||
fdma_addr += SOLO_DISP_BUF_SIZE;
|
||||
}
|
||||
error = 0;
|
||||
|
||||
finish_buf:
|
||||
if (error) {
|
||||
vb->state = VIDEOBUF_ERROR;
|
||||
} else {
|
||||
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) {
|
||||
videobuf_dma_contig_free(vq, vb);
|
||||
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)
|
||||
{
|
||||
videobuf_dma_contig_free(vq, vb);
|
||||
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;
|
||||
|
||||
if ((fh = kzalloc(sizeof(*fh), GFP_KERNEL)) == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&fh->slock);
|
||||
INIT_LIST_HEAD(&fh->vidq_active);
|
||||
fh->solo_dev = solo_dev;
|
||||
file->private_data = fh;
|
||||
|
||||
if ((ret = solo_start_thread(fh))) {
|
||||
kfree(fh);
|
||||
return ret;
|
||||
}
|
||||
|
||||
videobuf_queue_dma_contig_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);
|
||||
|
||||
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_M;
|
||||
|
||||
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_M,
|
||||
.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 >= 0)
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com
|
||||
* Copyright (C) 2010 Ben Collins <bcollins@bluecherry.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SOLO6010_H
|
||||
#define __SOLO6010_H
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/videobuf-core.h>
|
||||
|
||||
#include "solo6010-registers.h"
|
||||
|
||||
#ifndef PCI_VENDOR_ID_SOFTLOGIC
|
||||
#define PCI_VENDOR_ID_SOFTLOGIC 0x9413
|
||||
#define PCI_DEVICE_ID_SOLO6010 0x6010
|
||||
#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
|
||||
/* Commell Softlogic 6010 based cards */
|
||||
#define PCI_DEVICE_ID_COMMSOLO_4 0x4E04
|
||||
#define PCI_DEVICE_ID_COMMSOLO_9 0x4E09
|
||||
#define PCI_DEVICE_ID_COMMSOLO_16 0x4E10
|
||||
#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)
|
||||
|
||||
/*
|
||||
* 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
|
||||
#define SOLO_P2M_DESC_SIZE (SOLO_NR_P2M_DESC * 16)
|
||||
/* 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 solo_p2m_dev {
|
||||
struct semaphore sem;
|
||||
struct completion completion;
|
||||
int error;
|
||||
u8 desc[SOLO_P2M_DESC_SIZE];
|
||||
};
|
||||
|
||||
#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 irq_mask;
|
||||
u32 motion_mask;
|
||||
spinlock_t reg_io_lock;
|
||||
|
||||
/* tw28xx accounting */
|
||||
u8 tw2864, tw2815;
|
||||
u8 tw28_cnt;
|
||||
|
||||
/* i2c related items */
|
||||
struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS];
|
||||
enum SOLO_I2C_STATE i2c_state;
|
||||
struct semaphore i2c_sem;
|
||||
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);
|
||||
|
||||
/* 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 */
|
Loading…
Reference in New Issue