Commit faa4fd2a authored by Ben Collins's avatar Ben Collins Committed by Greg Kroah-Hartman
Browse files

Staging: solo6x10: New driver (staging) for Softlogic 6x10



This driver supports Softlogic 6x10 based codec cards

Signed-off-by: default avatarBen Collins <bcollins@bluecherry.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 412b2d08
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
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.
+6 −0
Original line number Diff line number Diff line
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
+282 −0
Original line number Diff line number Diff line
/*
 * 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);
+271 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+229 −0
Original line number Diff line number Diff line
/*
 * 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);
	}
}
Loading