Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,279 @@
#
# Platform drivers
# All drivers here are currently for webcam support
menuconfig V4L_PLATFORM_DRIVERS
bool "V4L platform devices"
depends on MEDIA_CAMERA_SUPPORT
default n
---help---
Say Y here to enable support for platform-specific V4L drivers.
if V4L_PLATFORM_DRIVERS
source "drivers/media/platform/marvell-ccic/Kconfig"
config VIDEO_VIA_CAMERA
tristate "VIAFB camera controller support"
depends on FB_VIA
select VIDEOBUF_DMA_SG
select VIDEO_OV7670
help
Driver support for the integrated camera controller in VIA
Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
with ov7670 sensors.
#
# Platform multimedia device configuration
#
source "drivers/media/platform/davinci/Kconfig"
source "drivers/media/platform/omap/Kconfig"
source "drivers/media/platform/blackfin/Kconfig"
source "drivers/media/platform/exynos/Kconfig"
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on MEDIA_CAMERA_SUPPORT
depends on VIDEO_DEV && I2C && HAS_DMA
depends on ARCH_SHMOBILE || COMPILE_TEST
select VIDEOBUF_DMA_CONTIG
help
Support for the Video Output Unit (VOU) on SuperH SoCs.
config VIDEO_VIU
tristate "Freescale VIU Video Driver"
depends on VIDEO_V4L2 && PPC_MPC512x
select VIDEOBUF_DMA_CONTIG
default y
---help---
Support for Freescale VIU video driver. This device captures
video data, or overlays video on DIU frame buffer.
Say Y here if you want to enable VIU device on MPC5121e Rev2+.
In doubt, say N.
config VIDEO_TIMBERDALE
tristate "Support for timberdale Video In/LogiWIN"
depends on VIDEO_V4L2 && I2C
depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST
select VIDEO_ADV7180
select VIDEOBUF_DMA_CONTIG
---help---
Add support for the Video In peripherial of the timberdale FPGA.
config VIDEO_VINO
tristate "SGI Vino Video For Linux"
depends on I2C && SGI_IP22 && VIDEO_V4L2
select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to build in support for the Vino video input system found
on SGI Indy machines.
config VIDEO_M32R_AR
tristate "AR devices"
depends on VIDEO_V4L2
depends on M32R || COMPILE_TEST
---help---
This is a video4linux driver for the Renesas AR (Artificial Retina)
camera module.
config VIDEO_M32R_AR_M64278
tristate "AR device with color module M64278(VGA)"
depends on PLAT_M32700UT
select VIDEO_M32R_AR
---help---
This is a video4linux driver for the Renesas AR (Artificial
Retina) with M64278E-800 camera module.
This module supports VGA(640x480 pixels) resolutions.
To compile this driver as a module, choose M here: the
module will be called arv.
config VIDEO_OMAP3
tristate "OMAP 3 Camera support"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
depends on HAS_DMA
select ARM_DMA_USE_IOMMU
select OMAP_IOMMU
select VIDEOBUF2_DMA_CONTIG
---help---
Driver for an OMAP 3 camera controller.
config VIDEO_OMAP3_DEBUG
bool "OMAP 3 Camera debug messages"
depends on VIDEO_OMAP3
---help---
Enable debug messages on OMAP 3 camera controller driver.
config VIDEO_S3C_CAMIF
tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
depends on PM_RUNTIME
depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
---help---
This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
host interface (CAMIF).
To compile this driver as a module, choose M here: the module
will be called s3c-camif.
source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/s5p-tv/Kconfig"
endif # V4L_PLATFORM_DRIVERS
menuconfig V4L_MEM2MEM_DRIVERS
bool "Memory-to-memory multimedia devices"
depends on VIDEO_V4L2
depends on MEDIA_CAMERA_SUPPORT
default n
---help---
Say Y here to enable selecting drivers for V4L devices that
use system memory for both source and destination buffers, as opposed
to capture and output drivers, which use memory buffers for just
one of those.
if V4L_MEM2MEM_DRIVERS
config VIDEO_CODA
tristate "Chips&Media Coda multi-standard codec IP"
depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC
depends on HAS_DMA
select SRAM
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select GENERIC_ALLOCATOR
---help---
Coda is a range of video codec IPs that supports
H.264, MPEG-4, and other video formats.
config VIDEO_MEM2MEM_DEINTERLACE
tristate "Deinterlace support"
depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
Generic deinterlacing V4L2 driver.
config VIDEO_SAMSUNG_S5P_G2D
tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
default n
---help---
This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D
2d graphics accelerator.
config VIDEO_SAMSUNG_S5P_JPEG
tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
This is a v4l2 driver for Samsung S5P, EXYNOS3250
and EXYNOS4 JPEG codec
config VIDEO_SAMSUNG_S5P_MFC
tristate "Samsung S5P MFC Video Codec"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
default n
help
MFC 5.1 and 6.x driver for V4L2
config VIDEO_MX2_EMMAPRP
tristate "MX2 eMMa-PrP support"
depends on VIDEO_DEV && VIDEO_V4L2
depends on SOC_IMX27 || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
MX2X chips have a PrP that can be used to process buffers from
memory to memory. Operations include resizing and format
conversion.
config VIDEO_SAMSUNG_EXYNOS_GSC
tristate "Samsung Exynos G-Scaler driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_EXYNOS5 || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler.
config VIDEO_SH_VEU
tristate "SuperH VEU mem2mem video processing driver"
depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
Support for the Video Engine Unit (VEU) on SuperH and
SH-Mobile SoCs.
config VIDEO_RENESAS_VSP1
tristate "Renesas VSP1 Video Processing Engine"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
depends on ARCH_SHMOBILE || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
---help---
This is a V4L2 driver for the Renesas VSP1 video processing engine.
To compile this driver as a module, choose M here: the module
will be called vsp1.
config VIDEO_TI_VPE
tristate "TI VPE (Video Processing Engine) driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on SOC_DRA7XX || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
default n
---help---
Support for the TI VPE(Video Processing Engine) block
found on DRA7XX SoC.
config VIDEO_TI_VPE_DEBUG
bool "VPE debug messages"
depends on VIDEO_TI_VPE
---help---
Enable debug messages on VPE driver.
endif # V4L_MEM2MEM_DRIVERS
menuconfig V4L_TEST_DRIVERS
bool "Media test drivers"
depends on MEDIA_CAMERA_SUPPORT
if V4L_TEST_DRIVERS
source "drivers/media/platform/vivid/Kconfig"
config VIDEO_MEM2MEM_TESTDEV
tristate "Virtual test device for mem2mem framework"
depends on VIDEO_DEV && VIDEO_V4L2
select VIDEOBUF2_VMALLOC
select V4L2_MEM2MEM_DEV
default n
---help---
This is a virtual test device for the memory-to-memory driver
framework.
endif #V4L_TEST_DRIVERS

View file

@ -0,0 +1,53 @@
#
# Makefile for the video capture/playback device drivers.
#
obj-$(CONFIG_VIDEO_VINO) += indycam.o
obj-$(CONFIG_VIDEO_VINO) += vino.o
obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/
obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
obj-$(CONFIG_VIDEO_VIVID) += vivid/
obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
obj-$(CONFIG_VIDEO_CODA) += coda/
obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/
obj-$(CONFIG_VIDEO_EXYNOS) += exynos/
obj-$(CONFIG_BLACKFIN) += blackfin/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
obj-y += omap/
ccflags-y += -I$(srctree)/drivers/media/i2c

View file

@ -0,0 +1,884 @@
/*
* Colour AR M64278(VGA) driver for Video4Linux
*
* Copyright (C) 2003 Takeo Takahashi <takahashi.takeo@renesas.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Some code is taken from AR driver sample program for M3T-M32700UT.
*
* AR driver sample (M32R SDK):
* Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION
* AND RENESAS SOLUTIONS CORPORATION
* All Rights Reserved.
*
* 2003-09-01: Support w3cam by Takeo Takahashi
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-fh.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <asm/m32r.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
#if 0
#define DEBUG(n, args...) printk(KERN_INFO args)
#define CHECK_LOST 1
#else
#define DEBUG(n, args...)
#define CHECK_LOST 0
#endif
/*
* USE_INT is always 0, interrupt mode is not available
* on linux due to lack of speed
*/
#define USE_INT 0 /* Don't modify */
#define VERSION "0.0.5"
#define ar_inl(addr) inl((unsigned long)(addr))
#define ar_outl(val, addr) outl((unsigned long)(val), (unsigned long)(addr))
extern struct cpuinfo_m32r boot_cpu_data;
/*
* CCD pixel size
* Note that M32700UT does not support CIF mode, but QVGA is
* supported by M32700UT hardware using VGA mode of AR LSI.
*
* Supported: VGA (Normal mode, Interlace mode)
* QVGA (Always Interlace mode of VGA)
*
*/
#define AR_WIDTH_VGA 640
#define AR_HEIGHT_VGA 480
#define AR_WIDTH_QVGA 320
#define AR_HEIGHT_QVGA 240
#define MIN_AR_WIDTH AR_WIDTH_QVGA
#define MIN_AR_HEIGHT AR_HEIGHT_QVGA
#define MAX_AR_WIDTH AR_WIDTH_VGA
#define MAX_AR_HEIGHT AR_HEIGHT_VGA
/* bits & bytes per pixel */
#define AR_BITS_PER_PIXEL 16
#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL / 8)
/* line buffer size */
#define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL)
#define AR_LINE_BYTES_QVGA (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL)
#define MAX_AR_LINE_BYTES AR_LINE_BYTES_VGA
/* frame size & type */
#define AR_FRAME_BYTES_VGA \
(AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL)
#define AR_FRAME_BYTES_QVGA \
(AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL)
#define MAX_AR_FRAME_BYTES \
(MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL)
#define AR_MAX_FRAME 15
/* capture size */
#define AR_SIZE_VGA 0
#define AR_SIZE_QVGA 1
/* capture mode */
#define AR_MODE_INTERLACE 0
#define AR_MODE_NORMAL 1
struct ar {
struct v4l2_device v4l2_dev;
struct video_device vdev;
int start_capture; /* duaring capture in INT. mode. */
#if USE_INT
unsigned char *line_buff; /* DMA line buffer */
#endif
unsigned char *frame[MAX_AR_HEIGHT]; /* frame data */
short size; /* capture size */
short mode; /* capture mode */
int width, height;
int frame_bytes, line_bytes;
wait_queue_head_t wait;
struct mutex lock;
};
static struct ar ardev;
static int video_nr = -1; /* video device number (first free) */
static unsigned char yuv[MAX_AR_FRAME_BYTES];
/* module parameters */
/* default frequency */
#define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */
static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */
static int vga; /* default mode(0:QVGA mode, other:VGA mode) */
static int vga_interlace; /* 0 is normal mode for, else interlace mode */
module_param(freq, int, 0);
module_param(vga, int, 0);
module_param(vga_interlace, int, 0);
static void wait_for_vsync(void)
{
while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */
cpu_relax();
while (!(ar_inl(ARVCR0) & ARVCR0_VDS)) /* wait for VSYNC */
cpu_relax();
}
static void wait_acknowledge(void)
{
int i;
for (i = 0; i < 1000; i++)
cpu_relax();
while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK)
cpu_relax();
}
/*******************************************************************
* I2C functions
*******************************************************************/
static void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2,
unsigned long data3)
{
int i;
/* Slave Address */
ar_outl(addr, PLDI2CDATA);
wait_for_vsync();
/* Start */
ar_outl(1, PLDI2CCND);
wait_acknowledge();
/* Transfer data 1 */
ar_outl(data1, PLDI2CDATA);
wait_for_vsync();
ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
wait_acknowledge();
/* Transfer data 2 */
ar_outl(data2, PLDI2CDATA);
wait_for_vsync();
ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
wait_acknowledge();
if (n == 3) {
/* Transfer data 3 */
ar_outl(data3, PLDI2CDATA);
wait_for_vsync();
ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
wait_acknowledge();
}
/* Stop */
for (i = 0; i < 100; i++)
cpu_relax();
ar_outl(2, PLDI2CCND);
ar_outl(2, PLDI2CCND);
while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB)
cpu_relax();
}
static void init_iic(void)
{
DEBUG(1, "init_iic:\n");
/*
* ICU Setting (iic)
*/
/* I2C Setting */
ar_outl(0x0, PLDI2CCR); /* I2CCR Disable */
ar_outl(0x0300, PLDI2CMOD); /* I2CMOD ACK/8b-data/7b-addr/auto */
ar_outl(0x1, PLDI2CACK); /* I2CACK ACK */
/* I2C CLK */
/* 50MH-100k */
if (freq == 75)
ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */
else if (freq == 50)
ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */
else
ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */
ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */
}
/**************************************************************************
*
* Video4Linux Interface functions
*
**************************************************************************/
static inline void disable_dma(void)
{
ar_outl(0x8000, M32R_DMAEN_PORTL); /* disable DMA0 */
}
static inline void enable_dma(void)
{
ar_outl(0x8080, M32R_DMAEN_PORTL); /* enable DMA0 */
}
static inline void clear_dma_status(void)
{
ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */
}
static void wait_for_vertical_sync(struct ar *ar, int exp_line)
{
#if CHECK_LOST
int tmout = 10000; /* FIXME */
int l;
/*
* check HCOUNT because we cannot check vertical sync.
*/
for (; tmout >= 0; tmout--) {
l = ar_inl(ARVHCOUNT);
if (l == exp_line)
break;
}
if (tmout < 0)
v4l2_err(&ar->v4l2_dev, "lost %d -> %d\n", exp_line, l);
#else
while (ar_inl(ARVHCOUNT) != exp_line)
cpu_relax();
#endif
}
static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
struct ar *ar = video_drvdata(file);
long ret = ar->frame_bytes; /* return read bytes */
unsigned long arvcr1 = 0;
unsigned long flags;
unsigned char *p;
int h, w;
unsigned char *py, *pu, *pv;
#if !USE_INT
int l;
#endif
DEBUG(1, "ar_read()\n");
if (ar->size == AR_SIZE_QVGA)
arvcr1 |= ARVCR1_QVGA;
if (ar->mode == AR_MODE_NORMAL)
arvcr1 |= ARVCR1_NORMAL;
mutex_lock(&ar->lock);
#if USE_INT
local_irq_save(flags);
disable_dma();
ar_outl(0xa1871300, M32R_DMA0CR0_PORTL);
ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
/* set AR FIFO address as source(BSEL5) */
ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* destination addr. */
ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL); /* reload address */
ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); /* byte count (bytes) */
ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */
/*
* Okay, kick AR LSI to invoke an interrupt
*/
ar->start_capture = -1;
ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1);
local_irq_restore(flags);
/* .... AR interrupts .... */
wait_event_interruptible(ar->wait, ar->start_capture == 0);
if (signal_pending(current)) {
printk(KERN_ERR "arv: interrupted while get frame data.\n");
ret = -EINTR;
goto out_up;
}
#else /* ! USE_INT */
/* polling */
ar_outl(arvcr1, ARVCR1);
disable_dma();
ar_outl(0x8000, M32R_DMAEDET_PORTL);
ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL);
ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL);
local_irq_save(flags);
while (ar_inl(ARVHCOUNT) != 0) /* wait for 0 */
cpu_relax();
if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
for (h = 0; h < ar->height; h++) {
wait_for_vertical_sync(ar, h);
if (h < (AR_HEIGHT_VGA/2))
l = h << 1;
else
l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1);
ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL);
enable_dma();
while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
cpu_relax();
disable_dma();
clear_dma_status();
ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
}
} else {
for (h = 0; h < ar->height; h++) {
wait_for_vertical_sync(ar, h);
ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL);
enable_dma();
while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
cpu_relax();
disable_dma();
clear_dma_status();
ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
}
}
local_irq_restore(flags);
#endif /* ! USE_INT */
/*
* convert YUV422 to YUV422P
* +--------------------+
* | Y0,Y1,... |
* | ..............Yn |
* +--------------------+
* | U0,U1,........Un |
* +--------------------+
* | V0,V1,........Vn |
* +--------------------+
*/
py = yuv;
pu = py + (ar->frame_bytes / 2);
pv = pu + (ar->frame_bytes / 4);
for (h = 0; h < ar->height; h++) {
p = ar->frame[h];
for (w = 0; w < ar->line_bytes; w += 4) {
*py++ = *p++;
*pu++ = *p++;
*py++ = *p++;
*pv++ = *p++;
}
}
if (copy_to_user(buf, yuv, ar->frame_bytes)) {
v4l2_err(&ar->v4l2_dev, "failed while copy_to_user yuv.\n");
ret = -EFAULT;
goto out_up;
}
DEBUG(1, "ret = %d\n", ret);
out_up:
mutex_unlock(&ar->lock);
return ret;
}
static int ar_querycap(struct file *file, void *priv,
struct v4l2_capability *vcap)
{
struct ar *ar = video_drvdata(file);
strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card));
strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info));
vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int ar_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
{
if (vin->index > 0)
return -EINVAL;
strlcpy(vin->name, "Camera", sizeof(vin->name));
vin->type = V4L2_INPUT_TYPE_CAMERA;
vin->audioset = 0;
vin->tuner = 0;
vin->std = V4L2_STD_ALL;
vin->status = 0;
return 0;
}
static int ar_g_input(struct file *file, void *fh, unsigned int *inp)
{
*inp = 0;
return 0;
}
static int ar_s_input(struct file *file, void *fh, unsigned int inp)
{
return inp ? -EINVAL : 0;
}
static int ar_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct ar *ar = video_drvdata(file);
struct v4l2_pix_format *pix = &fmt->fmt.pix;
pix->width = ar->width;
pix->height = ar->height;
pix->pixelformat = V4L2_PIX_FMT_YUV422P;
pix->field = (ar->mode == AR_MODE_NORMAL) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED;
pix->bytesperline = ar->width;
pix->sizeimage = 2 * ar->width * ar->height;
/* Just a guess */
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
static int ar_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct ar *ar = video_drvdata(file);
struct v4l2_pix_format *pix = &fmt->fmt.pix;
if (pix->height <= AR_HEIGHT_QVGA || pix->width <= AR_WIDTH_QVGA) {
pix->height = AR_HEIGHT_QVGA;
pix->width = AR_WIDTH_QVGA;
pix->field = V4L2_FIELD_INTERLACED;
} else {
pix->height = AR_HEIGHT_VGA;
pix->width = AR_WIDTH_VGA;
pix->field = vga_interlace ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
}
pix->pixelformat = V4L2_PIX_FMT_YUV422P;
pix->bytesperline = ar->width;
pix->sizeimage = 2 * ar->width * ar->height;
/* Just a guess */
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
static int ar_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct ar *ar = video_drvdata(file);
struct v4l2_pix_format *pix = &fmt->fmt.pix;
int ret = ar_try_fmt_vid_cap(file, fh, fmt);
if (ret)
return ret;
mutex_lock(&ar->lock);
ar->width = pix->width;
ar->height = pix->height;
if (ar->width == AR_WIDTH_VGA) {
ar->size = AR_SIZE_VGA;
ar->frame_bytes = AR_FRAME_BYTES_VGA;
ar->line_bytes = AR_LINE_BYTES_VGA;
if (vga_interlace)
ar->mode = AR_MODE_INTERLACE;
else
ar->mode = AR_MODE_NORMAL;
} else {
ar->size = AR_SIZE_QVGA;
ar->frame_bytes = AR_FRAME_BYTES_QVGA;
ar->line_bytes = AR_LINE_BYTES_QVGA;
ar->mode = AR_MODE_INTERLACE;
}
/* Ok we figured out what to use from our wide choice */
mutex_unlock(&ar->lock);
return 0;
}
static int ar_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
{
static struct v4l2_fmtdesc formats[] = {
{ 0, 0, 0,
"YUV 4:2:2 Planar", V4L2_PIX_FMT_YUV422P,
{ 0, 0, 0, 0 }
},
};
enum v4l2_buf_type type = fmt->type;
if (fmt->index > 0)
return -EINVAL;
*fmt = formats[fmt->index];
fmt->type = type;
return 0;
}
#if USE_INT
/*
* Interrupt handler
*/
static void ar_interrupt(int irq, void *dev)
{
struct ar *ar = dev;
unsigned int line_count;
unsigned int line_number;
unsigned int arvcr1;
line_count = ar_inl(ARVHCOUNT); /* line number */
if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
/* operations for interlace mode */
if (line_count < (AR_HEIGHT_VGA / 2)) /* even line */
line_number = (line_count << 1);
else /* odd line */
line_number =
(((line_count - (AR_HEIGHT_VGA / 2)) << 1) + 1);
} else {
line_number = line_count;
}
if (line_number == 0) {
/*
* It is an interrupt for line 0.
* we have to start capture.
*/
disable_dma();
#if 0
ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */
#endif
memcpy(ar->frame[0], ar->line_buff, ar->line_bytes);
#if 0
ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
#endif
enable_dma();
ar->start_capture = 1; /* during capture */
return;
}
if (ar->start_capture == 1 && line_number <= (ar->height - 1)) {
disable_dma();
memcpy(ar->frame[line_number], ar->line_buff, ar->line_bytes);
/*
* if captured all line of a frame, disable AR interrupt
* and wake a process up.
*/
if (line_number == (ar->height - 1)) { /* end of line */
ar->start_capture = 0;
/* disable AR interrupt request */
arvcr1 = ar_inl(ARVCR1);
arvcr1 &= ~ARVCR1_HIEN; /* clear int. flag */
ar_outl(arvcr1, ARVCR1); /* disable */
wake_up_interruptible(&ar->wait);
} else {
#if 0
ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL);
ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
#endif
enable_dma();
}
}
}
#endif
/*
* ar_initialize()
* ar_initialize() is called by video_register_device() and
* initializes AR LSI and peripherals.
*
* -1 is returned in all failures.
* 0 is returned in success.
*
*/
static int ar_initialize(struct ar *ar)
{
unsigned long cr = 0;
int i, found = 0;
DEBUG(1, "ar_initialize:\n");
/*
* initialize AR LSI
*/
ar_outl(0, ARVCR0); /* assert reset of AR LSI */
for (i = 0; i < 0x18; i++) /* wait for over 10 cycles @ 27MHz */
cpu_relax();
ar_outl(ARVCR0_RST, ARVCR0); /* negate reset of AR LSI (enable) */
for (i = 0; i < 0x40d; i++) /* wait for over 420 cycles @ 27MHz */
cpu_relax();
/* AR uses INT3 of CPU as interrupt pin. */
ar_outl(ARINTSEL_INT3, ARINTSEL);
if (ar->size == AR_SIZE_QVGA)
cr |= ARVCR1_QVGA;
if (ar->mode == AR_MODE_NORMAL)
cr |= ARVCR1_NORMAL;
ar_outl(cr, ARVCR1);
/*
* Initialize IIC so that CPU can communicate with AR LSI,
* and send boot commands to AR LSI.
*/
init_iic();
for (i = 0; i < 0x100000; i++) { /* > 0xa1d10, 56ms */
if ((ar_inl(ARVCR0) & ARVCR0_VDS)) { /* VSYNC */
found = 1;
break;
}
}
if (found == 0)
return -ENODEV;
v4l2_info(&ar->v4l2_dev, "Initializing ");
iic(2, 0x78, 0x11, 0x01, 0x00); /* start */
iic(3, 0x78, 0x12, 0x00, 0x06);
iic(3, 0x78, 0x12, 0x12, 0x30);
iic(3, 0x78, 0x12, 0x15, 0x58);
iic(3, 0x78, 0x12, 0x17, 0x30);
printk(KERN_CONT ".");
iic(3, 0x78, 0x12, 0x1a, 0x97);
iic(3, 0x78, 0x12, 0x1b, 0xff);
iic(3, 0x78, 0x12, 0x1c, 0xff);
iic(3, 0x78, 0x12, 0x26, 0x10);
iic(3, 0x78, 0x12, 0x27, 0x00);
printk(KERN_CONT ".");
iic(2, 0x78, 0x34, 0x02, 0x00);
iic(2, 0x78, 0x7a, 0x10, 0x00);
iic(2, 0x78, 0x80, 0x39, 0x00);
iic(2, 0x78, 0x81, 0xe6, 0x00);
iic(2, 0x78, 0x8d, 0x00, 0x00);
printk(KERN_CONT ".");
iic(2, 0x78, 0x8e, 0x0c, 0x00);
iic(2, 0x78, 0x8f, 0x00, 0x00);
#if 0
iic(2, 0x78, 0x90, 0x00, 0x00); /* AWB on=1 off=0 */
#endif
iic(2, 0x78, 0x93, 0x01, 0x00);
iic(2, 0x78, 0x94, 0xcd, 0x00);
iic(2, 0x78, 0x95, 0x00, 0x00);
printk(KERN_CONT ".");
iic(2, 0x78, 0x96, 0xa0, 0x00);
iic(2, 0x78, 0x97, 0x00, 0x00);
iic(2, 0x78, 0x98, 0x60, 0x00);
iic(2, 0x78, 0x99, 0x01, 0x00);
iic(2, 0x78, 0x9a, 0x19, 0x00);
printk(KERN_CONT ".");
iic(2, 0x78, 0x9b, 0x02, 0x00);
iic(2, 0x78, 0x9c, 0xe8, 0x00);
iic(2, 0x78, 0x9d, 0x02, 0x00);
iic(2, 0x78, 0x9e, 0x2e, 0x00);
iic(2, 0x78, 0xb8, 0x78, 0x00);
iic(2, 0x78, 0xba, 0x05, 0x00);
#if 0
iic(2, 0x78, 0x83, 0x8c, 0x00); /* brightness */
#endif
printk(KERN_CONT ".");
/* color correction */
iic(3, 0x78, 0x49, 0x00, 0x95); /* a */
iic(3, 0x78, 0x49, 0x01, 0x96); /* b */
iic(3, 0x78, 0x49, 0x03, 0x85); /* c */
iic(3, 0x78, 0x49, 0x04, 0x97); /* d */
iic(3, 0x78, 0x49, 0x02, 0x7e); /* e(Lo) */
iic(3, 0x78, 0x49, 0x05, 0xa4); /* f(Lo) */
iic(3, 0x78, 0x49, 0x06, 0x04); /* e(Hi) */
iic(3, 0x78, 0x49, 0x07, 0x04); /* e(Hi) */
iic(2, 0x78, 0x48, 0x01, 0x00); /* on=1 off=0 */
printk(KERN_CONT ".");
iic(2, 0x78, 0x11, 0x00, 0x00); /* end */
printk(KERN_CONT " done\n");
return 0;
}
/****************************************************************************
*
* Video4Linux Module functions
*
****************************************************************************/
static const struct v4l2_file_operations ar_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = v4l2_fh_release,
.read = ar_read,
.unlocked_ioctl = video_ioctl2,
};
static const struct v4l2_ioctl_ops ar_ioctl_ops = {
.vidioc_querycap = ar_querycap,
.vidioc_g_input = ar_g_input,
.vidioc_s_input = ar_s_input,
.vidioc_enum_input = ar_enum_input,
.vidioc_enum_fmt_vid_cap = ar_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = ar_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = ar_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = ar_try_fmt_vid_cap,
};
#define ALIGN4(x) ((((int)(x)) & 0x3) == 0)
static int __init ar_init(void)
{
struct ar *ar;
struct v4l2_device *v4l2_dev;
int ret;
int i;
ar = &ardev;
v4l2_dev = &ar->v4l2_dev;
strlcpy(v4l2_dev->name, "arv", sizeof(v4l2_dev->name));
v4l2_info(v4l2_dev, "Colour AR VGA driver %s\n", VERSION);
ret = v4l2_device_register(NULL, v4l2_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
return ret;
}
ret = -EIO;
#if USE_INT
/* allocate a DMA buffer for 1 line. */
ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA);
if (ar->line_buff == NULL || !ALIGN4(ar->line_buff)) {
v4l2_err(v4l2_dev, "buffer allocation failed for DMA.\n");
ret = -ENOMEM;
goto out_end;
}
#endif
/* allocate buffers for a frame */
for (i = 0; i < MAX_AR_HEIGHT; i++) {
ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL);
if (ar->frame[i] == NULL || !ALIGN4(ar->frame[i])) {
v4l2_err(v4l2_dev, "buffer allocation failed for frame.\n");
ret = -ENOMEM;
goto out_line_buff;
}
}
strlcpy(ar->vdev.name, "Colour AR VGA", sizeof(ar->vdev.name));
ar->vdev.v4l2_dev = v4l2_dev;
ar->vdev.fops = &ar_fops;
ar->vdev.ioctl_ops = &ar_ioctl_ops;
ar->vdev.release = video_device_release_empty;
video_set_drvdata(&ar->vdev, ar);
if (vga) {
ar->width = AR_WIDTH_VGA;
ar->height = AR_HEIGHT_VGA;
ar->size = AR_SIZE_VGA;
ar->frame_bytes = AR_FRAME_BYTES_VGA;
ar->line_bytes = AR_LINE_BYTES_VGA;
if (vga_interlace)
ar->mode = AR_MODE_INTERLACE;
else
ar->mode = AR_MODE_NORMAL;
} else {
ar->width = AR_WIDTH_QVGA;
ar->height = AR_HEIGHT_QVGA;
ar->size = AR_SIZE_QVGA;
ar->frame_bytes = AR_FRAME_BYTES_QVGA;
ar->line_bytes = AR_LINE_BYTES_QVGA;
ar->mode = AR_MODE_INTERLACE;
}
mutex_init(&ar->lock);
init_waitqueue_head(&ar->wait);
#if USE_INT
if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) {
v4l2_err("request_irq(%d) failed.\n", M32R_IRQ_INT3);
ret = -EIO;
goto out_irq;
}
#endif
if (ar_initialize(ar) != 0) {
v4l2_err(v4l2_dev, "M64278 not found.\n");
ret = -ENODEV;
goto out_dev;
}
/*
* ok, we can initialize h/w according to parameters,
* so register video device as a frame grabber type.
* device is named "video[0-64]".
* video_register_device() initializes h/w using ar_initialize().
*/
if (video_register_device(&ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) {
/* return -1, -ENFILE(full) or others */
v4l2_err(v4l2_dev, "register video (Colour AR) failed.\n");
ret = -ENODEV;
goto out_dev;
}
v4l2_info(v4l2_dev, "%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n",
video_device_node_name(&ar->vdev), M32R_IRQ_INT3, freq);
return 0;
out_dev:
#if USE_INT
free_irq(M32R_IRQ_INT3, ar);
out_irq:
#endif
for (i = 0; i < MAX_AR_HEIGHT; i++)
kfree(ar->frame[i]);
out_line_buff:
#if USE_INT
kfree(ar->line_buff);
out_end:
#endif
v4l2_device_unregister(&ar->v4l2_dev);
return ret;
}
static int __init ar_init_module(void)
{
freq = (boot_cpu_data.bus_clock / 1000000);
printk(KERN_INFO "arv: Bus clock %d\n", freq);
if (freq != 50 && freq != 75)
freq = DEFAULT_FREQ;
return ar_init();
}
static void __exit ar_cleanup_module(void)
{
struct ar *ar;
int i;
ar = &ardev;
video_unregister_device(&ar->vdev);
#if USE_INT
free_irq(M32R_IRQ_INT3, ar);
#endif
for (i = 0; i < MAX_AR_HEIGHT; i++)
kfree(ar->frame[i]);
#if USE_INT
kfree(ar->line_buff);
#endif
v4l2_device_unregister(&ar->v4l2_dev);
}
module_init(ar_init_module);
module_exit(ar_cleanup_module);
MODULE_AUTHOR("Takeo Takahashi <takahashi.takeo@renesas.com>");
MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);

View file

@ -0,0 +1,16 @@
config VIDEO_BLACKFIN_CAPTURE
tristate "Blackfin Video Capture Driver"
depends on VIDEO_V4L2 && BLACKFIN && I2C
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
help
V4L2 bridge driver for Blackfin video capture device.
Choose PPI or EPPI as its interface.
To compile this driver as a module, choose M here: the
module will be called bfin_capture.
config VIDEO_BLACKFIN_PPI
tristate
depends on VIDEO_BLACKFIN_CAPTURE
default VIDEO_BLACKFIN_CAPTURE

View file

@ -0,0 +1,2 @@
obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o
obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,364 @@
/*
* ppi.c Analog Devices Parallel Peripheral Interface driver
*
* Copyright (c) 2011 Analog Devices Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <asm/bfin_ppi.h>
#include <asm/blackfin.h>
#include <asm/cacheflush.h>
#include <asm/dma.h>
#include <asm/portmux.h>
#include <media/blackfin/ppi.h>
static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
static void ppi_detach_irq(struct ppi_if *ppi);
static int ppi_start(struct ppi_if *ppi);
static int ppi_stop(struct ppi_if *ppi);
static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
static const struct ppi_ops ppi_ops = {
.attach_irq = ppi_attach_irq,
.detach_irq = ppi_detach_irq,
.start = ppi_start,
.stop = ppi_stop,
.set_params = ppi_set_params,
.update_addr = ppi_update_addr,
};
static irqreturn_t ppi_irq_err(int irq, void *dev_id)
{
struct ppi_if *ppi = dev_id;
const struct ppi_info *info = ppi->info;
switch (info->type) {
case PPI_TYPE_PPI:
{
struct bfin_ppi_regs *reg = info->base;
unsigned short status;
/* register on bf561 is cleared when read
* others are W1C
*/
status = bfin_read16(&reg->status);
if (status & 0x3000)
ppi->err = true;
bfin_write16(&reg->status, 0xff00);
break;
}
case PPI_TYPE_EPPI:
{
struct bfin_eppi_regs *reg = info->base;
unsigned short status;
status = bfin_read16(&reg->status);
if (status & 0x2)
ppi->err = true;
bfin_write16(&reg->status, 0xffff);
break;
}
case PPI_TYPE_EPPI3:
{
struct bfin_eppi3_regs *reg = info->base;
unsigned long stat;
stat = bfin_read32(&reg->stat);
if (stat & 0x2)
ppi->err = true;
bfin_write32(&reg->stat, 0xc0ff);
break;
}
default:
break;
}
return IRQ_HANDLED;
}
static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
{
const struct ppi_info *info = ppi->info;
int ret;
ret = request_dma(info->dma_ch, "PPI_DMA");
if (ret) {
pr_err("Unable to allocate DMA channel for PPI\n");
return ret;
}
set_dma_callback(info->dma_ch, handler, ppi);
if (ppi->err_int) {
ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi);
if (ret) {
pr_err("Unable to allocate IRQ for PPI\n");
free_dma(info->dma_ch);
}
}
return ret;
}
static void ppi_detach_irq(struct ppi_if *ppi)
{
const struct ppi_info *info = ppi->info;
if (ppi->err_int)
free_irq(info->irq_err, ppi);
free_dma(info->dma_ch);
}
static int ppi_start(struct ppi_if *ppi)
{
const struct ppi_info *info = ppi->info;
/* enable DMA */
enable_dma(info->dma_ch);
/* enable PPI */
ppi->ppi_control |= PORT_EN;
switch (info->type) {
case PPI_TYPE_PPI:
{
struct bfin_ppi_regs *reg = info->base;
bfin_write16(&reg->control, ppi->ppi_control);
break;
}
case PPI_TYPE_EPPI:
{
struct bfin_eppi_regs *reg = info->base;
bfin_write32(&reg->control, ppi->ppi_control);
break;
}
case PPI_TYPE_EPPI3:
{
struct bfin_eppi3_regs *reg = info->base;
bfin_write32(&reg->ctl, ppi->ppi_control);
break;
}
default:
return -EINVAL;
}
SSYNC();
return 0;
}
static int ppi_stop(struct ppi_if *ppi)
{
const struct ppi_info *info = ppi->info;
/* disable PPI */
ppi->ppi_control &= ~PORT_EN;
switch (info->type) {
case PPI_TYPE_PPI:
{
struct bfin_ppi_regs *reg = info->base;
bfin_write16(&reg->control, ppi->ppi_control);
break;
}
case PPI_TYPE_EPPI:
{
struct bfin_eppi_regs *reg = info->base;
bfin_write32(&reg->control, ppi->ppi_control);
break;
}
case PPI_TYPE_EPPI3:
{
struct bfin_eppi3_regs *reg = info->base;
bfin_write32(&reg->ctl, ppi->ppi_control);
break;
}
default:
return -EINVAL;
}
/* disable DMA */
clear_dma_irqstat(info->dma_ch);
disable_dma(info->dma_ch);
SSYNC();
return 0;
}
static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
{
const struct ppi_info *info = ppi->info;
int dma32 = 0;
int dma_config, bytes_per_line;
int hcount, hdelay, samples_per_line;
#ifdef CONFIG_PINCTRL
static const char * const pin_state[] = {"8bit", "16bit", "24bit"};
struct pinctrl *pctrl;
struct pinctrl_state *pstate;
if (params->dlen > 24 || params->dlen <= 0)
return -EINVAL;
pctrl = devm_pinctrl_get(ppi->dev);
pstate = pinctrl_lookup_state(pctrl,
pin_state[(params->dlen + 7) / 8 - 1]);
if (pinctrl_select_state(pctrl, pstate))
return -EINVAL;
#endif
bytes_per_line = params->width * params->bpp / 8;
/* convert parameters unit from pixels to samples */
hcount = params->width * params->bpp / params->dlen;
hdelay = params->hdelay * params->bpp / params->dlen;
samples_per_line = params->line * params->bpp / params->dlen;
if (params->int_mask == 0xFFFFFFFF)
ppi->err_int = false;
else
ppi->err_int = true;
dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y);
ppi->ppi_control = params->ppi_control & ~PORT_EN;
if (!(ppi->ppi_control & PORT_DIR))
dma_config |= WNR;
switch (info->type) {
case PPI_TYPE_PPI:
{
struct bfin_ppi_regs *reg = info->base;
if (params->ppi_control & DMA32)
dma32 = 1;
bfin_write16(&reg->control, ppi->ppi_control);
bfin_write16(&reg->count, samples_per_line - 1);
bfin_write16(&reg->frame, params->frame);
break;
}
case PPI_TYPE_EPPI:
{
struct bfin_eppi_regs *reg = info->base;
if ((params->ppi_control & PACK_EN)
|| (params->ppi_control & 0x38000) > DLEN_16)
dma32 = 1;
bfin_write32(&reg->control, ppi->ppi_control);
bfin_write16(&reg->line, samples_per_line);
bfin_write16(&reg->frame, params->frame);
bfin_write16(&reg->hdelay, hdelay);
bfin_write16(&reg->vdelay, params->vdelay);
bfin_write16(&reg->hcount, hcount);
bfin_write16(&reg->vcount, params->height);
break;
}
case PPI_TYPE_EPPI3:
{
struct bfin_eppi3_regs *reg = info->base;
if ((params->ppi_control & PACK_EN)
|| (params->ppi_control & 0x70000) > DLEN_16)
dma32 = 1;
bfin_write32(&reg->ctl, ppi->ppi_control);
bfin_write32(&reg->line, samples_per_line);
bfin_write32(&reg->frame, params->frame);
bfin_write32(&reg->hdly, hdelay);
bfin_write32(&reg->vdly, params->vdelay);
bfin_write32(&reg->hcnt, hcount);
bfin_write32(&reg->vcnt, params->height);
if (params->int_mask)
bfin_write32(&reg->imsk, params->int_mask & 0xFF);
if (ppi->ppi_control & PORT_DIR) {
u32 hsync_width, vsync_width, vsync_period;
hsync_width = params->hsync
* params->bpp / params->dlen;
vsync_width = params->vsync * samples_per_line;
vsync_period = samples_per_line * params->frame;
bfin_write32(&reg->fs1_wlhb, hsync_width);
bfin_write32(&reg->fs1_paspl, samples_per_line);
bfin_write32(&reg->fs2_wlvb, vsync_width);
bfin_write32(&reg->fs2_palpf, vsync_period);
}
break;
}
default:
return -EINVAL;
}
if (dma32) {
dma_config |= WDSIZE_32 | PSIZE_32;
set_dma_x_count(info->dma_ch, bytes_per_line >> 2);
set_dma_x_modify(info->dma_ch, 4);
set_dma_y_modify(info->dma_ch, 4);
} else {
dma_config |= WDSIZE_16 | PSIZE_16;
set_dma_x_count(info->dma_ch, bytes_per_line >> 1);
set_dma_x_modify(info->dma_ch, 2);
set_dma_y_modify(info->dma_ch, 2);
}
set_dma_y_count(info->dma_ch, params->height);
set_dma_config(info->dma_ch, dma_config);
SSYNC();
return 0;
}
static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
{
set_dma_start_addr(ppi->info->dma_ch, addr);
}
struct ppi_if *ppi_create_instance(struct platform_device *pdev,
const struct ppi_info *info)
{
struct ppi_if *ppi;
if (!info || !info->pin_req)
return NULL;
#ifndef CONFIG_PINCTRL
if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
dev_err(&pdev->dev, "request peripheral failed\n");
return NULL;
}
#endif
ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
if (!ppi) {
peripheral_free_list(info->pin_req);
dev_err(&pdev->dev, "unable to allocate memory for ppi handle\n");
return NULL;
}
ppi->ops = &ppi_ops;
ppi->info = info;
ppi->dev = &pdev->dev;
pr_info("ppi probe success\n");
return ppi;
}
EXPORT_SYMBOL(ppi_create_instance);
void ppi_delete_instance(struct ppi_if *ppi)
{
peripheral_free_list(ppi->info->pin_req);
kfree(ppi);
}
EXPORT_SYMBOL(ppi_delete_instance);
MODULE_DESCRIPTION("Analog Devices PPI driver");
MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,3 @@
coda-objs := coda-common.o coda-bit.o coda-h264.o
obj-$(CONFIG_VIDEO_CODA) += coda.o

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
/*
* Coda multi-standard codec IP - H.264 helper functions
*
* Copyright (C) 2012 Vista Silicon S.L.
* Javier Martin, <javier.martin@vista-silicon.com>
* Xavier Duret
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/string.h>
static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 };
static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 };
int coda_h264_padding(int size, char *p)
{
int nal_size;
int diff;
diff = size - (size & ~0x7);
if (diff == 0)
return 0;
nal_size = coda_filler_size[diff];
memcpy(p, coda_filler_nal, nal_size);
/* Add rbsp stop bit and trailing at the end */
*(p + nal_size - 1) = 0x80;
return nal_size;
}

View file

@ -0,0 +1,287 @@
/*
* Coda multi-standard codec IP
*
* Copyright (C) 2012 Vista Silicon S.L.
* Javier Martin, <javier.martin@vista-silicon.com>
* Xavier Duret
* Copyright (C) 2012-2014 Philipp Zabel, Pengutronix
*
* 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.
*/
#include <linux/debugfs.h>
#include <linux/irqreturn.h>
#include <linux/mutex.h>
#include <linux/kfifo.h>
#include <linux/videodev2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/videobuf2-core.h>
#include "coda_regs.h"
#define CODA_MAX_FRAMEBUFFERS 8
#define CODA_MAX_FRAME_SIZE 0x100000
#define FMO_SLICE_SAVE_BUF_SIZE (32)
enum {
V4L2_M2M_SRC = 0,
V4L2_M2M_DST = 1,
};
enum coda_inst_type {
CODA_INST_ENCODER,
CODA_INST_DECODER,
};
enum coda_product {
CODA_DX6 = 0xf001,
CODA_7541 = 0xf012,
CODA_960 = 0xf020,
};
struct coda_devtype {
char *firmware;
enum coda_product product;
const struct coda_codec *codecs;
unsigned int num_codecs;
size_t workbuf_size;
size_t tempbuf_size;
size_t iram_size;
};
struct coda_aux_buf {
void *vaddr;
dma_addr_t paddr;
u32 size;
struct debugfs_blob_wrapper blob;
struct dentry *dentry;
};
struct coda_dev {
struct v4l2_device v4l2_dev;
struct video_device vfd[2];
struct platform_device *plat_dev;
const struct coda_devtype *devtype;
void __iomem *regs_base;
struct clk *clk_per;
struct clk *clk_ahb;
struct reset_control *rstc;
struct coda_aux_buf codebuf;
struct coda_aux_buf tempbuf;
struct coda_aux_buf workbuf;
struct gen_pool *iram_pool;
struct coda_aux_buf iram;
spinlock_t irqlock;
struct mutex dev_mutex;
struct mutex coda_mutex;
struct workqueue_struct *workqueue;
struct v4l2_m2m_dev *m2m_dev;
struct vb2_alloc_ctx *alloc_ctx;
struct list_head instances;
unsigned long instance_mask;
struct dentry *debugfs_root;
};
struct coda_codec {
u32 mode;
u32 src_fourcc;
u32 dst_fourcc;
u32 max_w;
u32 max_h;
};
struct coda_huff_tab;
struct coda_params {
u8 rot_mode;
u8 h264_intra_qp;
u8 h264_inter_qp;
u8 h264_min_qp;
u8 h264_max_qp;
u8 h264_deblk_enabled;
u8 h264_deblk_alpha;
u8 h264_deblk_beta;
u8 mpeg4_intra_qp;
u8 mpeg4_inter_qp;
u8 gop_size;
int intra_refresh;
int codec_mode;
int codec_mode_aux;
enum v4l2_mpeg_video_multi_slice_mode slice_mode;
u32 framerate;
u16 bitrate;
u32 slice_max_bits;
u32 slice_max_mb;
};
struct coda_timestamp {
struct list_head list;
u32 sequence;
struct v4l2_timecode timecode;
struct timeval timestamp;
};
/* Per-queue, driver-specific private data */
struct coda_q_data {
unsigned int width;
unsigned int height;
unsigned int bytesperline;
unsigned int sizeimage;
unsigned int fourcc;
struct v4l2_rect rect;
};
struct coda_iram_info {
u32 axi_sram_use;
phys_addr_t buf_bit_use;
phys_addr_t buf_ip_ac_dc_use;
phys_addr_t buf_dbk_y_use;
phys_addr_t buf_dbk_c_use;
phys_addr_t buf_ovl_use;
phys_addr_t buf_btp_use;
phys_addr_t search_ram_paddr;
int search_ram_size;
int remaining;
phys_addr_t next_paddr;
};
struct gdi_tiled_map {
int xy2ca_map[16];
int xy2ba_map[16];
int xy2ra_map[16];
int rbc2axi_map[32];
int xy2rbc_config;
int map_type;
#define GDI_LINEAR_FRAME_MAP 0
};
struct coda_ctx;
struct coda_context_ops {
int (*queue_init)(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq);
int (*start_streaming)(struct coda_ctx *ctx);
int (*prepare_run)(struct coda_ctx *ctx);
void (*finish_run)(struct coda_ctx *ctx);
void (*seq_end_work)(struct work_struct *work);
void (*release)(struct coda_ctx *ctx);
};
struct coda_ctx {
struct coda_dev *dev;
struct mutex buffer_mutex;
struct list_head list;
struct work_struct pic_run_work;
struct work_struct seq_end_work;
struct completion completion;
const struct coda_context_ops *ops;
int aborting;
int initialized;
int streamon_out;
int streamon_cap;
u32 isequence;
u32 qsequence;
u32 osequence;
u32 sequence_offset;
struct coda_q_data q_data[2];
enum coda_inst_type inst_type;
const struct coda_codec *codec;
enum v4l2_colorspace colorspace;
struct coda_params params;
struct v4l2_ctrl_handler ctrls;
struct v4l2_fh fh;
int gopcounter;
int runcounter;
char vpu_header[3][64];
int vpu_header_size[3];
struct kfifo bitstream_fifo;
struct mutex bitstream_mutex;
struct coda_aux_buf bitstream;
bool hold;
struct coda_aux_buf parabuf;
struct coda_aux_buf psbuf;
struct coda_aux_buf slicebuf;
struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS];
u32 frame_types[CODA_MAX_FRAMEBUFFERS];
struct coda_timestamp frame_timestamps[CODA_MAX_FRAMEBUFFERS];
u32 frame_errors[CODA_MAX_FRAMEBUFFERS];
struct list_head timestamp_list;
struct coda_aux_buf workbuf;
int num_internal_frames;
int idx;
int reg_idx;
struct coda_iram_info iram_info;
struct gdi_tiled_map tiled_map;
u32 bit_stream_param;
u32 frm_dis_flg;
u32 frame_mem_ctrl;
int display_idx;
struct dentry *debugfs_entry;
};
extern int coda_debug;
void coda_write(struct coda_dev *dev, u32 data, u32 reg);
unsigned int coda_read(struct coda_dev *dev, u32 reg);
int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf,
size_t size, const char *name, struct dentry *parent);
void coda_free_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf);
static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
struct coda_aux_buf *buf, size_t size,
const char *name)
{
return coda_alloc_aux_buf(ctx->dev, buf, size, name, ctx->debugfs_entry);
}
int coda_encoder_queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq);
int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq);
int coda_hw_reset(struct coda_ctx *ctx);
void coda_fill_bitstream(struct coda_ctx *ctx);
void coda_set_gdi_regs(struct coda_ctx *ctx);
static inline struct coda_q_data *get_q_data(struct coda_ctx *ctx,
enum v4l2_buf_type type)
{
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return &(ctx->q_data[V4L2_M2M_SRC]);
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return &(ctx->q_data[V4L2_M2M_DST]);
default:
return NULL;
}
}
const char *coda_product_name(int product);
int coda_check_firmware(struct coda_dev *dev);
static inline int coda_get_bitstream_payload(struct coda_ctx *ctx)
{
return kfifo_len(&ctx->bitstream_fifo);
}
void coda_bit_stream_end_flag(struct coda_ctx *ctx);
int coda_h264_padding(int size, char *p);
extern const struct coda_context_ops coda_bit_encode_ops;
extern const struct coda_context_ops coda_bit_decode_ops;
irqreturn_t coda_irq_handler(int irq, void *data);

View file

@ -0,0 +1,447 @@
/*
* linux/drivers/media/platform/coda/coda_regs.h
*
* Copyright (C) 2012 Vista Silicon SL
* Javier Martin <javier.martin@vista-silicon.com>
* Xavier Duret
*
* 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.
*/
#ifndef _REGS_CODA_H_
#define _REGS_CODA_H_
/* HW registers */
#define CODA_REG_BIT_CODE_RUN 0x000
#define CODA_REG_RUN_ENABLE (1 << 0)
#define CODA_REG_BIT_CODE_DOWN 0x004
#define CODA_DOWN_ADDRESS_SET(x) (((x) & 0xffff) << 16)
#define CODA_DOWN_DATA_SET(x) ((x) & 0xffff)
#define CODA_REG_BIT_HOST_IN_REQ 0x008
#define CODA_REG_BIT_INT_CLEAR 0x00c
#define CODA_REG_BIT_INT_CLEAR_SET 0x1
#define CODA_REG_BIT_INT_STATUS 0x010
#define CODA_REG_BIT_CODE_RESET 0x014
#define CODA_REG_RESET_ENABLE (1 << 0)
#define CODA_REG_BIT_CUR_PC 0x018
#define CODA9_REG_BIT_SW_RESET 0x024
#define CODA9_SW_RESET_BPU_CORE 0x008
#define CODA9_SW_RESET_BPU_BUS 0x010
#define CODA9_SW_RESET_VCE_CORE 0x020
#define CODA9_SW_RESET_VCE_BUS 0x040
#define CODA9_SW_RESET_GDI_CORE 0x080
#define CODA9_SW_RESET_GDI_BUS 0x100
#define CODA9_REG_BIT_SW_RESET_STATUS 0x034
/* Static SW registers */
#define CODA_REG_BIT_CODE_BUF_ADDR 0x100
#define CODA_REG_BIT_WORK_BUF_ADDR 0x104
#define CODA_REG_BIT_PARA_BUF_ADDR 0x108
#define CODA_REG_BIT_STREAM_CTRL 0x10c
#define CODA7_STREAM_BUF_PIC_RESET (1 << 4)
#define CODADX6_STREAM_BUF_PIC_RESET (1 << 3)
#define CODA7_STREAM_BUF_PIC_FLUSH (1 << 3)
#define CODADX6_STREAM_BUF_PIC_FLUSH (1 << 2)
#define CODA7_STREAM_BUF_DYNALLOC_EN (1 << 5)
#define CODADX6_STREAM_BUF_DYNALLOC_EN (1 << 4)
#define CODADX6_STREAM_CHKDIS_OFFSET (1 << 1)
#define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1)
#define CODA_STREAM_ENDIAN_SELECT (1 << 0)
#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110
#define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2)
#define CODA_IMAGE_ENDIAN_SELECT (1 << 0)
#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114
#define CODA_BIT_STREAM_END_FLAG (1 << 2)
#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0)
#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118
#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x))
#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x))
#define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x))
#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140
#define CODA7_REG_BIT_AXI_SRAM_USE 0x140
#define CODA9_USE_HOST_BTP_ENABLE (1 << 13)
#define CODA9_USE_HOST_OVL_ENABLE (1 << 12)
#define CODA7_USE_HOST_ME_ENABLE (1 << 11)
#define CODA9_USE_HOST_DBK_ENABLE (3 << 10)
#define CODA7_USE_HOST_OVL_ENABLE (1 << 10)
#define CODA7_USE_HOST_DBK_ENABLE (1 << 9)
#define CODA9_USE_HOST_IP_ENABLE (1 << 9)
#define CODA7_USE_HOST_IP_ENABLE (1 << 8)
#define CODA9_USE_HOST_BIT_ENABLE (1 << 8)
#define CODA7_USE_HOST_BIT_ENABLE (1 << 7)
#define CODA9_USE_BTP_ENABLE (1 << 5)
#define CODA7_USE_ME_ENABLE (1 << 4)
#define CODA9_USE_OVL_ENABLE (1 << 4)
#define CODA7_USE_OVL_ENABLE (1 << 3)
#define CODA9_USE_DBK_ENABLE (3 << 2)
#define CODA7_USE_DBK_ENABLE (1 << 2)
#define CODA7_USE_IP_ENABLE (1 << 1)
#define CODA7_USE_BIT_ENABLE (1 << 0)
#define CODA_REG_BIT_BUSY 0x160
#define CODA_REG_BIT_BUSY_FLAG 1
#define CODA_REG_BIT_RUN_COMMAND 0x164
#define CODA_COMMAND_SEQ_INIT 1
#define CODA_COMMAND_SEQ_END 2
#define CODA_COMMAND_PIC_RUN 3
#define CODA_COMMAND_SET_FRAME_BUF 4
#define CODA_COMMAND_ENCODE_HEADER 5
#define CODA_COMMAND_ENC_PARA_SET 6
#define CODA_COMMAND_DEC_PARA_SET 7
#define CODA_COMMAND_DEC_BUF_FLUSH 8
#define CODA_COMMAND_RC_CHANGE_PARAMETER 9
#define CODA_COMMAND_FIRMWARE_GET 0xf
#define CODA_REG_BIT_RUN_INDEX 0x168
#define CODA_INDEX_SET(x) ((x) & 0x3)
#define CODA_REG_BIT_RUN_COD_STD 0x16c
#define CODADX6_MODE_DECODE_MP4 0
#define CODADX6_MODE_ENCODE_MP4 1
#define CODADX6_MODE_DECODE_H264 2
#define CODADX6_MODE_ENCODE_H264 3
#define CODA7_MODE_DECODE_H264 0
#define CODA7_MODE_DECODE_VC1 1
#define CODA7_MODE_DECODE_MP2 2
#define CODA7_MODE_DECODE_MP4 3
#define CODA7_MODE_DECODE_DV3 3
#define CODA7_MODE_DECODE_RV 4
#define CODA7_MODE_DECODE_MJPG 5
#define CODA7_MODE_ENCODE_H264 8
#define CODA7_MODE_ENCODE_MP4 11
#define CODA7_MODE_ENCODE_MJPG 13
#define CODA9_MODE_DECODE_H264 0
#define CODA9_MODE_DECODE_VC1 1
#define CODA9_MODE_DECODE_MP2 2
#define CODA9_MODE_DECODE_MP4 3
#define CODA9_MODE_DECODE_DV3 3
#define CODA9_MODE_DECODE_RV 4
#define CODA9_MODE_DECODE_AVS 5
#define CODA9_MODE_DECODE_MJPG 6
#define CODA9_MODE_DECODE_VPX 7
#define CODA9_MODE_ENCODE_H264 8
#define CODA9_MODE_ENCODE_MP4 11
#define CODA9_MODE_ENCODE_MJPG 13
#define CODA_MODE_INVALID 0xffff
#define CODA_REG_BIT_INT_ENABLE 0x170
#define CODA_INT_INTERRUPT_ENABLE (1 << 3)
#define CODA_REG_BIT_INT_REASON 0x174
#define CODA7_REG_BIT_RUN_AUX_STD 0x178
#define CODA_MP4_AUX_MPEG4 0
#define CODA_MP4_AUX_DIVX3 1
#define CODA_VPX_AUX_THO 0
#define CODA_VPX_AUX_VP6 1
#define CODA_VPX_AUX_VP8 2
#define CODA_H264_AUX_AVC 0
#define CODA_H264_AUX_MVC 1
/*
* Commands' mailbox:
* registers with offsets in the range 0x180-0x1d0
* have different meaning depending on the command being
* issued.
*/
/* Decoder Sequence Initialization */
#define CODA_CMD_DEC_SEQ_BB_START 0x180
#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184
#define CODA_CMD_DEC_SEQ_OPTION 0x188
#define CODA_REORDER_ENABLE (1 << 1)
#define CODADX6_QP_REPORT (1 << 0)
#define CODA7_MP4_DEBLK_ENABLE (1 << 0)
#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c
#define CODA_CMD_DEC_SEQ_START_BYTE 0x190
#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194
#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198
#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c
#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c
#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0
#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0
#define CODA9_RET_DEC_SEQ_BITRATE 0x1b4
#define CODA_RET_DEC_SEQ_SUCCESS 0x1c0
#define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */
#define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4
#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8
#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8
#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc
#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0
#define CODA_RET_DEC_SEQ_INFO 0x1d4
#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8
#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc
#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0
#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0
#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4
#define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8
#define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4
#define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8
#define CODA9_RET_DEC_SEQ_HEADER_REPORT 0x1ec
/* Decoder Picture Run */
#define CODA_CMD_DEC_PIC_ROT_MODE 0x180
#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184
#define CODA9_CMD_DEC_PIC_ROT_INDEX 0x184
#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188
#define CODA9_CMD_DEC_PIC_ROT_ADDR_Y 0x188
#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c
#define CODA9_CMD_DEC_PIC_ROT_ADDR_CB 0x18c
#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190
#define CODA9_CMD_DEC_PIC_ROT_ADDR_CR 0x190
#define CODA9_CMD_DEC_PIC_ROT_STRIDE 0x1b8
#define CODA_CMD_DEC_PIC_OPTION 0x194
#define CODA_PRE_SCAN_EN (1 << 0)
#define CODA_PRE_SCAN_MODE_DECODE (0 << 1)
#define CODA_PRE_SCAN_MODE_RETURN (1 << 1)
#define CODA_IFRAME_SEARCH_EN (1 << 2)
#define CODA_SKIP_FRAME_MODE (0x3 << 3)
#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198
#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c
#define CODA_CMD_DEC_PIC_BB_START 0x1a0
#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4
#define CODA_RET_DEC_PIC_SIZE 0x1bc
#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0
#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4
#define CODA_RET_DEC_PIC_ERR_MB 0x1c8
#define CODA_RET_DEC_PIC_TYPE 0x1cc
#define CODA_PIC_TYPE_MASK 0x7
#define CODA_PIC_TYPE_MASK_VC1 0x3f
#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3)
#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6)
#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16)
#define CODA7_PIC_TYPE_INTERLACED (1 << 18)
#define CODA_RET_DEC_PIC_POST 0x1d0
#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0
#define CODA_RET_DEC_PIC_OPTION 0x1d4
#define CODA_RET_DEC_PIC_SUCCESS 0x1d8
#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc
#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0
#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4
#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec
#define CODA9_RET_DEC_PIC_VP8_PIC_REPORT 0x1e8
#define CODA9_RET_DEC_PIC_ASPECT 0x1f0
#define CODA9_RET_DEC_PIC_VP8_SCALE_INFO 0x1f0
#define CODA9_RET_DEC_PIC_FRATE_NR 0x1f4
#define CODA9_RET_DEC_PIC_FRATE_DR 0x1f8
/* Encoder Sequence Initialization */
#define CODA_CMD_ENC_SEQ_BB_START 0x180
#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184
#define CODA_CMD_ENC_SEQ_OPTION 0x188
#define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9
#define CODA9_OPTION_MVC_PREFIX_NAL_OFFSET 9
#define CODA7_OPTION_GAMMA_OFFSET 8
#define CODA9_OPTION_MVC_PARASET_REFRESH_OFFSET 8
#define CODA7_OPTION_RCQPMAX_OFFSET 7
#define CODA9_OPTION_GAMMA_OFFSET 7
#define CODADX6_OPTION_GAMMA_OFFSET 7
#define CODA7_OPTION_RCQPMIN_OFFSET 6
#define CODA9_OPTION_RCQPMAX_OFFSET 6
#define CODA_OPTION_LIMITQP_OFFSET 6
#define CODA_OPTION_RCINTRAQP_OFFSET 5
#define CODA_OPTION_FMO_OFFSET 4
#define CODA9_OPTION_MVC_INTERVIEW_OFFSET 4
#define CODA_OPTION_AVC_AUD_OFFSET 2
#define CODA_OPTION_SLICEREPORT_OFFSET 1
#define CODA_CMD_ENC_SEQ_COD_STD 0x18c
#define CODA_STD_MPEG4 0
#define CODA9_STD_H264 0
#define CODA_STD_H263 1
#define CODA_STD_H264 2
#define CODA_STD_MJPG 3
#define CODA9_STD_MPEG4 3
#define CODA_CMD_ENC_SEQ_SRC_SIZE 0x190
#define CODA7_PICWIDTH_OFFSET 16
#define CODA7_PICWIDTH_MASK 0xffff
#define CODADX6_PICWIDTH_OFFSET 10
#define CODADX6_PICWIDTH_MASK 0x3ff
#define CODA_PICHEIGHT_OFFSET 0
#define CODADX6_PICHEIGHT_MASK 0x3ff
#define CODA7_PICHEIGHT_MASK 0xffff
#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194
#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198
#define CODA_MP4PARAM_VERID_OFFSET 6
#define CODA_MP4PARAM_VERID_MASK 0x01
#define CODA_MP4PARAM_INTRADCVLCTHR_OFFSET 2
#define CODA_MP4PARAM_INTRADCVLCTHR_MASK 0x07
#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_OFFSET 1
#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_MASK 0x01
#define CODA_MP4PARAM_DATAPARTITIONENABLE_OFFSET 0
#define CODA_MP4PARAM_DATAPARTITIONENABLE_MASK 0x01
#define CODA_CMD_ENC_SEQ_263_PARA 0x19c
#define CODA_263PARAM_ANNEXJENABLE_OFFSET 2
#define CODA_263PARAM_ANNEXJENABLE_MASK 0x01
#define CODA_263PARAM_ANNEXKENABLE_OFFSET 1
#define CODA_263PARAM_ANNEXKENABLE_MASK 0x01
#define CODA_263PARAM_ANNEXTENABLE_OFFSET 0
#define CODA_263PARAM_ANNEXTENABLE_MASK 0x01
#define CODA_CMD_ENC_SEQ_264_PARA 0x1a0
#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET 12
#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK 0x0f
#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET 8
#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK 0x0f
#define CODA_264PARAM_DISABLEDEBLK_OFFSET 6
#define CODA_264PARAM_DISABLEDEBLK_MASK 0x01
#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET 5
#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_MASK 0x01
#define CODA_264PARAM_CHROMAQPOFFSET_OFFSET 0
#define CODA_264PARAM_CHROMAQPOFFSET_MASK 0x1f
#define CODA_CMD_ENC_SEQ_SLICE_MODE 0x1a4
#define CODA_SLICING_SIZE_OFFSET 2
#define CODA_SLICING_SIZE_MASK 0x3fffffff
#define CODA_SLICING_UNIT_OFFSET 1
#define CODA_SLICING_UNIT_MASK 0x01
#define CODA_SLICING_MODE_OFFSET 0
#define CODA_SLICING_MODE_MASK 0x01
#define CODA_CMD_ENC_SEQ_GOP_SIZE 0x1a8
#define CODA_GOP_SIZE_OFFSET 0
#define CODA_GOP_SIZE_MASK 0x3f
#define CODA_CMD_ENC_SEQ_RC_PARA 0x1ac
#define CODA_RATECONTROL_AUTOSKIP_OFFSET 31
#define CODA_RATECONTROL_AUTOSKIP_MASK 0x01
#define CODA_RATECONTROL_INITIALDELAY_OFFSET 16
#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7f
#define CODA_RATECONTROL_BITRATE_OFFSET 1
#define CODA_RATECONTROL_BITRATE_MASK 0x7f
#define CODA_RATECONTROL_ENABLE_OFFSET 0
#define CODA_RATECONTROL_ENABLE_MASK 0x01
#define CODA_CMD_ENC_SEQ_RC_BUF_SIZE 0x1b0
#define CODA_CMD_ENC_SEQ_INTRA_REFRESH 0x1b4
#define CODADX6_CMD_ENC_SEQ_FMO 0x1b8
#define CODA_FMOPARAM_TYPE_OFFSET 4
#define CODA_FMOPARAM_TYPE_MASK 1
#define CODA_FMOPARAM_SLICENUM_OFFSET 0
#define CODA_FMOPARAM_SLICENUM_MASK 0x0f
#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc
#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8
#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc
#define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4
#define CODA_CMD_ENC_SEQ_RC_QP_MIN_MAX 0x1c8
#define CODA_QPMIN_OFFSET 8
#define CODA_QPMIN_MASK 0x3f
#define CODA_QPMAX_OFFSET 0
#define CODA_QPMAX_MASK 0x3f
#define CODA_CMD_ENC_SEQ_RC_GAMMA 0x1cc
#define CODA_GAMMA_OFFSET 0
#define CODA_GAMMA_MASK 0xffff
#define CODA_CMD_ENC_SEQ_RC_INTERVAL_MODE 0x1d0
#define CODA9_CMD_ENC_SEQ_INTRA_WEIGHT 0x1d4
#define CODA9_CMD_ENC_SEQ_ME_OPTION 0x1d8
#define CODA_RET_ENC_SEQ_SUCCESS 0x1c0
/* Encoder Picture Run */
#define CODA9_CMD_ENC_PIC_SRC_INDEX 0x180
#define CODA9_CMD_ENC_PIC_SRC_STRIDE 0x184
#define CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC 0x1a4
#define CODA9_CMD_ENC_PIC_SRC_ADDR_Y 0x1a8
#define CODA9_CMD_ENC_PIC_SRC_ADDR_CB 0x1ac
#define CODA9_CMD_ENC_PIC_SRC_ADDR_CR 0x1b0
#define CODA_CMD_ENC_PIC_SRC_ADDR_Y 0x180
#define CODA_CMD_ENC_PIC_SRC_ADDR_CB 0x184
#define CODA_CMD_ENC_PIC_SRC_ADDR_CR 0x188
#define CODA_CMD_ENC_PIC_QS 0x18c
#define CODA_CMD_ENC_PIC_ROT_MODE 0x190
#define CODA_ROT_MIR_ENABLE (1 << 4)
#define CODA_ROT_0 (0x0 << 0)
#define CODA_ROT_90 (0x1 << 0)
#define CODA_ROT_180 (0x2 << 0)
#define CODA_ROT_270 (0x3 << 0)
#define CODA_MIR_NONE (0x0 << 2)
#define CODA_MIR_VER (0x1 << 2)
#define CODA_MIR_HOR (0x2 << 2)
#define CODA_MIR_VER_HOR (0x3 << 2)
#define CODA_CMD_ENC_PIC_OPTION 0x194
#define CODA_FORCE_IPICTURE BIT(1)
#define CODA_REPORT_MB_INFO BIT(3)
#define CODA_REPORT_MV_INFO BIT(4)
#define CODA_REPORT_SLICE_INFO BIT(5)
#define CODA_CMD_ENC_PIC_BB_START 0x198
#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c
#define CODA_RET_ENC_FRAME_NUM 0x1c0
#define CODA_RET_ENC_PIC_TYPE 0x1c4
#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8
#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc
#define CODA_RET_ENC_PIC_FLAG 0x1d0
#define CODA_RET_ENC_PIC_SUCCESS 0x1d8
/* Set Frame Buffer */
#define CODA_CMD_SET_FRAME_BUF_NUM 0x180
#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184
#define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188
#define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c
#define CODA9_CMD_SET_FRAME_SUBSAMP_A 0x188
#define CODA9_CMD_SET_FRAME_SUBSAMP_B 0x18c
#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190
#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194
#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198
#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c
#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0
#define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4
#define CODA9_CMD_SET_FRAME_AXI_BTP_ADDR 0x1a4
#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8
#define CODA9_CMD_SET_FRAME_CACHE_SIZE 0x1a8
#define CODA9_CMD_SET_FRAME_CACHE_CONFIG 0x1ac
#define CODA9_CACHE_BYPASS_OFFSET 28
#define CODA9_CACHE_DUALCONF_OFFSET 26
#define CODA9_CACHE_PAGEMERGE_OFFSET 24
#define CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET 16
#define CODA9_CACHE_CB_BUFFER_SIZE_OFFSET 8
#define CODA9_CACHE_CR_BUFFER_SIZE_OFFSET 0
#define CODA9_CMD_SET_FRAME_SUBSAMP_A_MVC 0x1b0
#define CODA9_CMD_SET_FRAME_SUBSAMP_B_MVC 0x1b4
#define CODA9_CMD_SET_FRAME_DP_BUF_BASE 0x1b0
#define CODA9_CMD_SET_FRAME_DP_BUF_SIZE 0x1b4
#define CODA9_CMD_SET_FRAME_MAX_DEC_SIZE 0x1b8
#define CODA9_CMD_SET_FRAME_DELAY 0x1bc
/* Encoder Header */
#define CODA_CMD_ENC_HEADER_CODE 0x180
#define CODA_GAMMA_OFFSET 0
#define CODA_HEADER_H264_SPS 0
#define CODA_HEADER_H264_PPS 1
#define CODA_HEADER_MP4V_VOL 0
#define CODA_HEADER_MP4V_VOS 1
#define CODA_HEADER_MP4V_VIS 2
#define CODA9_HEADER_FRAME_CROP (1 << 3)
#define CODA_CMD_ENC_HEADER_BB_START 0x184
#define CODA_CMD_ENC_HEADER_BB_SIZE 0x188
#define CODA9_CMD_ENC_HEADER_FRAME_CROP_H 0x18c
#define CODA9_CMD_ENC_HEADER_FRAME_CROP_V 0x190
/* Get Version */
#define CODA_CMD_FIRMWARE_VERNUM 0x1c0
#define CODA_FIRMWARE_PRODUCT(x) (((x) >> 16) & 0xffff)
#define CODA_FIRMWARE_MAJOR(x) (((x) >> 12) & 0x0f)
#define CODA_FIRMWARE_MINOR(x) (((x) >> 8) & 0x0f)
#define CODA_FIRMWARE_RELEASE(x) ((x) & 0xff)
#define CODA_FIRMWARE_VERNUM(product, major, minor, release) \
((product) << 16 | ((major) << 12) | \
((minor) << 8) | (release))
#define CODA9_CMD_FIRMWARE_CODE_REV 0x1c4
#define CODA9_GDMA_BASE 0x1000
#define CODA9_GDI_WPROT_ERR_CLR (CODA9_GDMA_BASE + 0x0a0)
#define CODA9_GDI_WPROT_RGN_EN (CODA9_GDMA_BASE + 0x0ac)
#define CODA9_GDI_BUS_CTRL (CODA9_GDMA_BASE + 0x0f0)
#define CODA9_GDI_BUS_STATUS (CODA9_GDMA_BASE + 0x0f4)
#define CODA9_GDI_XY2_CAS_0 (CODA9_GDMA_BASE + 0x800)
#define CODA9_GDI_XY2_CAS_F (CODA9_GDMA_BASE + 0x83c)
#define CODA9_GDI_XY2_BA_0 (CODA9_GDMA_BASE + 0x840)
#define CODA9_GDI_XY2_BA_1 (CODA9_GDMA_BASE + 0x844)
#define CODA9_GDI_XY2_BA_2 (CODA9_GDMA_BASE + 0x848)
#define CODA9_GDI_XY2_BA_3 (CODA9_GDMA_BASE + 0x84c)
#define CODA9_GDI_XY2_RAS_0 (CODA9_GDMA_BASE + 0x850)
#define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c)
#define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890)
#define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0)
#define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c)
#endif

View file

@ -0,0 +1,88 @@
config VIDEO_DAVINCI_VPIF_DISPLAY
tristate "TI DaVinci VPIF V4L2-Display driver"
depends on VIDEO_DEV
depends on ARCH_DAVINCI || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT
help
Enables Davinci VPIF module used for display devices.
This module is used for display on TI DM6467/DA850/OMAPL138
SoCs.
To compile this driver as a module, choose M here. There will
be two modules called vpif.ko and vpif_display.ko
config VIDEO_DAVINCI_VPIF_CAPTURE
tristate "TI DaVinci VPIF video capture driver"
depends on VIDEO_DEV
depends on ARCH_DAVINCI || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
help
Enables Davinci VPIF module used for capture devices.
This module is used for capture on TI DM6467/DA850/OMAPL138
SoCs.
To compile this driver as a module, choose M here. There will
be two modules called vpif.ko and vpif_capture.ko
config VIDEO_DM6446_CCDC
tristate "TI DM6446 CCDC video capture driver"
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF_DMA_CONTIG
help
Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
with decoder modules such as TVP5146 over BT656 or
sensor module such as MT9T001 over a raw interface. This
module configures the interface and CCDC/ISIF to do
video frame capture from slave decoders.
To compile this driver as a module, choose M here. There will
be three modules called vpfe_capture.ko, vpss.ko and dm644x_ccdc.ko
config VIDEO_DM355_CCDC
tristate "TI DM355 CCDC video capture driver"
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF_DMA_CONTIG
help
Enables DM355 CCD hw module. DM355 CCDC hw interfaces
with decoder modules such as TVP5146 over BT656 or
sensor module such as MT9T001 over a raw interface. This
module configures the interface and CCDC/ISIF to do
video frame capture from a slave decoders
To compile this driver as a module, choose M here. There will
be three modules called vpfe_capture.ko, vpss.ko and dm355_ccdc.ko
config VIDEO_DM365_ISIF
tristate "TI DM365 ISIF video capture driver"
depends on VIDEO_V4L2 && ARCH_DAVINCI
depends on HAS_DMA
select VIDEOBUF_DMA_CONTIG
help
Enables ISIF hw module. This is the hardware module for
configuring ISIF in VPFE to capture Raw Bayer RGB data from
a image sensor or YUV data from a YUV source.
To compile this driver as a module, choose M here. There will
be three modules called vpfe_capture.ko, vpss.ko and isif.ko
config VIDEO_DAVINCI_VPBE_DISPLAY
tristate "TI DaVinci VPBE V4L2-Display driver"
depends on ARCH_DAVINCI
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
help
Enables Davinci VPBE module used for display devices.
This module is used for display on TI DM644x/DM365/DM355
based display devices.
To compile this driver as a module, choose M here. There will
be five modules created called vpss.ko, vpbe.ko, vpbe_osd.ko,
vpbe_venc.ko and vpbe_display.ko

View file

@ -0,0 +1,15 @@
#
# Makefile for the davinci video device drivers.
#
#VPIF Display driver
obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif.o vpif_display.o
#VPIF Capture driver
obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif.o vpif_capture.o
# Capture: DM6446 and DM355
obj-$(CONFIG_VIDEO_DM6446_CCDC) += vpfe_capture.o vpss.o dm644x_ccdc.o
obj-$(CONFIG_VIDEO_DM355_CCDC) += vpfe_capture.o vpss.o dm355_ccdc.o
obj-$(CONFIG_VIDEO_DM365_ISIF) += vpfe_capture.o vpss.o isif.o
obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpss.o vpbe.o vpbe_osd.o \
vpbe_venc.o vpbe_display.o

View file

@ -0,0 +1,110 @@
/*
* Copyright (C) 2008-2009 Texas Instruments Inc
*
* 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
*
* ccdc device API
*/
#ifndef _CCDC_HW_DEVICE_H
#define _CCDC_HW_DEVICE_H
#ifdef __KERNEL__
#include <linux/videodev2.h>
#include <linux/device.h>
#include <media/davinci/vpfe_types.h>
#include <media/davinci/ccdc_types.h>
/*
* ccdc hw operations
*/
struct ccdc_hw_ops {
/* Pointer to initialize function to initialize ccdc device */
int (*open) (struct device *dev);
/* Pointer to deinitialize function */
int (*close) (struct device *dev);
/* set ccdc base address */
void (*set_ccdc_base)(void *base, int size);
/* Pointer to function to enable or disable ccdc */
void (*enable) (int en);
/* reset sbl. only for 6446 */
void (*reset) (void);
/* enable output to sdram */
void (*enable_out_to_sdram) (int en);
/* Pointer to function to set hw parameters */
int (*set_hw_if_params) (struct vpfe_hw_if_param *param);
/* get interface parameters */
int (*get_hw_if_params) (struct vpfe_hw_if_param *param);
/*
* Pointer to function to set parameters. Used
* for implementing VPFE_S_CCDC_PARAMS
*/
int (*set_params) (void *params);
/*
* Pointer to function to get parameter. Used
* for implementing VPFE_G_CCDC_PARAMS
*/
int (*get_params) (void *params);
/* Pointer to function to configure ccdc */
int (*configure) (void);
/* Pointer to function to set buffer type */
int (*set_buftype) (enum ccdc_buftype buf_type);
/* Pointer to function to get buffer type */
enum ccdc_buftype (*get_buftype) (void);
/* Pointer to function to set frame format */
int (*set_frame_format) (enum ccdc_frmfmt frm_fmt);
/* Pointer to function to get frame format */
enum ccdc_frmfmt (*get_frame_format) (void);
/* enumerate hw pix formats */
int (*enum_pix)(u32 *hw_pix, int i);
/* Pointer to function to set buffer type */
u32 (*get_pixel_format) (void);
/* Pointer to function to get pixel format. */
int (*set_pixel_format) (u32 pixfmt);
/* Pointer to function to set image window */
int (*set_image_window) (struct v4l2_rect *win);
/* Pointer to function to set image window */
void (*get_image_window) (struct v4l2_rect *win);
/* Pointer to function to get line length */
unsigned int (*get_line_length) (void);
/* Query CCDC control IDs */
int (*queryctrl)(struct v4l2_queryctrl *qctrl);
/* Set CCDC control */
int (*set_control)(struct v4l2_control *ctrl);
/* Get CCDC control */
int (*get_control)(struct v4l2_control *ctrl);
/* Pointer to function to set frame buffer address */
void (*setfbaddr) (unsigned long addr);
/* Pointer to function to get field id */
int (*getfid) (void);
};
struct ccdc_hw_device {
/* ccdc device name */
char name[32];
/* module owner */
struct module *owner;
/* hw ops */
struct ccdc_hw_ops hw_ops;
};
/* Used by CCDC module to register & unregister with vpfe capture driver */
int vpfe_register_ccdc_device(struct ccdc_hw_device *dev);
void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev);
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,310 @@
/*
* Copyright (C) 2005-2009 Texas Instruments Inc
*
* 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 _DM355_CCDC_REGS_H
#define _DM355_CCDC_REGS_H
/**************************************************************************\
* Register OFFSET Definitions
\**************************************************************************/
#define SYNCEN 0x00
#define MODESET 0x04
#define HDWIDTH 0x08
#define VDWIDTH 0x0c
#define PPLN 0x10
#define LPFR 0x14
#define SPH 0x18
#define NPH 0x1c
#define SLV0 0x20
#define SLV1 0x24
#define NLV 0x28
#define CULH 0x2c
#define CULV 0x30
#define HSIZE 0x34
#define SDOFST 0x38
#define STADRH 0x3c
#define STADRL 0x40
#define CLAMP 0x44
#define DCSUB 0x48
#define COLPTN 0x4c
#define BLKCMP0 0x50
#define BLKCMP1 0x54
#define MEDFILT 0x58
#define RYEGAIN 0x5c
#define GRCYGAIN 0x60
#define GBGGAIN 0x64
#define BMGGAIN 0x68
#define OFFSET 0x6c
#define OUTCLIP 0x70
#define VDINT0 0x74
#define VDINT1 0x78
#define RSV0 0x7c
#define GAMMAWD 0x80
#define REC656IF 0x84
#define CCDCFG 0x88
#define FMTCFG 0x8c
#define FMTPLEN 0x90
#define FMTSPH 0x94
#define FMTLNH 0x98
#define FMTSLV 0x9c
#define FMTLNV 0xa0
#define FMTRLEN 0xa4
#define FMTHCNT 0xa8
#define FMT_ADDR_PTR_B 0xac
#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4))
#define FMTPGM_VF0 0xcc
#define FMTPGM_VF1 0xd0
#define FMTPGM_AP0 0xd4
#define FMTPGM_AP1 0xd8
#define FMTPGM_AP2 0xdc
#define FMTPGM_AP3 0xe0
#define FMTPGM_AP4 0xe4
#define FMTPGM_AP5 0xe8
#define FMTPGM_AP6 0xec
#define FMTPGM_AP7 0xf0
#define LSCCFG1 0xf4
#define LSCCFG2 0xf8
#define LSCH0 0xfc
#define LSCV0 0x100
#define LSCKH 0x104
#define LSCKV 0x108
#define LSCMEMCTL 0x10c
#define LSCMEMD 0x110
#define LSCMEMQ 0x114
#define DFCCTL 0x118
#define DFCVSAT 0x11c
#define DFCMEMCTL 0x120
#define DFCMEM0 0x124
#define DFCMEM1 0x128
#define DFCMEM2 0x12c
#define DFCMEM3 0x130
#define DFCMEM4 0x134
#define CSCCTL 0x138
#define CSCM0 0x13c
#define CSCM1 0x140
#define CSCM2 0x144
#define CSCM3 0x148
#define CSCM4 0x14c
#define CSCM5 0x150
#define CSCM6 0x154
#define CSCM7 0x158
#define DATAOFST 0x15c
#define CCDC_REG_LAST DATAOFST
/**************************************************************
* Define for various register bit mask and shifts for CCDC
*
**************************************************************/
#define CCDC_RAW_IP_MODE 0
#define CCDC_VDHDOUT_INPUT 0
#define CCDC_YCINSWP_RAW (0 << 4)
#define CCDC_EXWEN_DISABLE 0
#define CCDC_DATAPOL_NORMAL 0
#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0
#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6)
#define CCDC_CCDCFG_WENLOG_AND 0
#define CCDC_CCDCFG_TRGSEL_WEN 0
#define CCDC_CCDCFG_EXTRG_DISABLE 0
#define CCDC_CFA_MOSAIC 0
#define CCDC_Y8POS_SHIFT 11
#define CCDC_VDC_DFCVSAT_MASK 0x3fff
#define CCDC_DATAOFST_MASK 0x0ff
#define CCDC_DATAOFST_H_SHIFT 0
#define CCDC_DATAOFST_V_SHIFT 8
#define CCDC_GAMMAWD_CFA_MASK 1
#define CCDC_GAMMAWD_CFA_SHIFT 5
#define CCDC_GAMMAWD_INPUT_SHIFT 2
#define CCDC_FID_POL_MASK 1
#define CCDC_FID_POL_SHIFT 4
#define CCDC_HD_POL_MASK 1
#define CCDC_HD_POL_SHIFT 3
#define CCDC_VD_POL_MASK 1
#define CCDC_VD_POL_SHIFT 2
#define CCDC_VD_POL_NEGATIVE (1 << 2)
#define CCDC_FRM_FMT_MASK 1
#define CCDC_FRM_FMT_SHIFT 7
#define CCDC_DATA_SZ_MASK 7
#define CCDC_DATA_SZ_SHIFT 8
#define CCDC_VDHDOUT_MASK 1
#define CCDC_VDHDOUT_SHIFT 0
#define CCDC_EXWEN_MASK 1
#define CCDC_EXWEN_SHIFT 5
#define CCDC_INPUT_MODE_MASK 3
#define CCDC_INPUT_MODE_SHIFT 12
#define CCDC_PIX_FMT_MASK 3
#define CCDC_PIX_FMT_SHIFT 12
#define CCDC_DATAPOL_MASK 1
#define CCDC_DATAPOL_SHIFT 6
#define CCDC_WEN_ENABLE (1 << 1)
#define CCDC_VDHDEN_ENABLE (1 << 16)
#define CCDC_LPF_ENABLE (1 << 14)
#define CCDC_ALAW_ENABLE 1
#define CCDC_ALAW_GAMMA_WD_MASK 7
#define CCDC_REC656IF_BT656_EN 3
#define CCDC_FMTCFG_FMTMODE_MASK 3
#define CCDC_FMTCFG_FMTMODE_SHIFT 1
#define CCDC_FMTCFG_LNUM_MASK 3
#define CCDC_FMTCFG_LNUM_SHIFT 4
#define CCDC_FMTCFG_ADDRINC_MASK 7
#define CCDC_FMTCFG_ADDRINC_SHIFT 8
#define CCDC_CCDCFG_FIDMD_SHIFT 6
#define CCDC_CCDCFG_WENLOG_SHIFT 8
#define CCDC_CCDCFG_TRGSEL_SHIFT 9
#define CCDC_CCDCFG_EXTRG_SHIFT 10
#define CCDC_CCDCFG_MSBINVI_SHIFT 13
#define CCDC_HSIZE_FLIP_SHIFT 12
#define CCDC_HSIZE_FLIP_MASK 1
#define CCDC_HSIZE_VAL_MASK 0xFFF
#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249
#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D
#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D
#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000
#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0
#define CCDC_START_PX_HOR_MASK 0x7FFF
#define CCDC_NUM_PX_HOR_MASK 0x7FFF
#define CCDC_START_VER_ONE_MASK 0x7FFF
#define CCDC_START_VER_TWO_MASK 0x7FFF
#define CCDC_NUM_LINES_VER 0x7FFF
#define CCDC_BLK_CLAMP_ENABLE (1 << 15)
#define CCDC_BLK_SGAIN_MASK 0x1F
#define CCDC_BLK_ST_PXL_MASK 0x1FFF
#define CCDC_BLK_SAMPLE_LN_MASK 3
#define CCDC_BLK_SAMPLE_LN_SHIFT 13
#define CCDC_NUM_LINE_CALC_MASK 3
#define CCDC_NUM_LINE_CALC_SHIFT 14
#define CCDC_BLK_DC_SUB_MASK 0x3FFF
#define CCDC_BLK_COMP_MASK 0xFF
#define CCDC_BLK_COMP_GB_COMP_SHIFT 8
#define CCDC_BLK_COMP_GR_COMP_SHIFT 0
#define CCDC_BLK_COMP_R_COMP_SHIFT 8
#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15)
#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15)
#define CCDC_FPC_ENABLE (1 << 15)
#define CCDC_FPC_FPC_NUM_MASK 0x7FFF
#define CCDC_DATA_PACK_ENABLE (1 << 11)
#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF
#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF
#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16
#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF
#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF
#define CCDC_FMT_VERT_FMTSLV_SHIFT 16
#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF
#define CCDC_VP_OUT_VERT_NUM_SHIFT 17
#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF
#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4
#define CCDC_VP_OUT_HORZ_ST_MASK 0xF
#define CCDC_CSC_COEF_INTEG_MASK 7
#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f
#define CCDC_CSC_COEF_INTEG_SHIFT 5
#define CCDC_CSCM_MSB_SHIFT 8
#define CCDC_CSC_ENABLE 1
#define CCDC_CSC_DEC_MAX 32
#define CCDC_MFILT1_SHIFT 10
#define CCDC_MFILT2_SHIFT 8
#define CCDC_MED_FILT_THRESH 0x3FFF
#define CCDC_LPF_MASK 1
#define CCDC_LPF_SHIFT 14
#define CCDC_OFFSET_MASK 0x3FF
#define CCDC_DATASFT_MASK 7
#define CCDC_DATASFT_SHIFT 8
#define CCDC_DF_ENABLE 1
#define CCDC_FMTPLEN_P0_MASK 0xF
#define CCDC_FMTPLEN_P1_MASK 0xF
#define CCDC_FMTPLEN_P2_MASK 7
#define CCDC_FMTPLEN_P3_MASK 7
#define CCDC_FMTPLEN_P0_SHIFT 0
#define CCDC_FMTPLEN_P1_SHIFT 4
#define CCDC_FMTPLEN_P2_SHIFT 8
#define CCDC_FMTPLEN_P3_SHIFT 12
#define CCDC_FMTSPH_MASK 0x1FFF
#define CCDC_FMTLNH_MASK 0x1FFF
#define CCDC_FMTSLV_MASK 0x1FFF
#define CCDC_FMTLNV_MASK 0x7FFF
#define CCDC_FMTRLEN_MASK 0x1FFF
#define CCDC_FMTHCNT_MASK 0x1FFF
#define CCDC_ADP_INIT_MASK 0x1FFF
#define CCDC_ADP_LINE_SHIFT 13
#define CCDC_ADP_LINE_MASK 3
#define CCDC_FMTPGN_APTR_MASK 7
#define CCDC_DFCCTL_GDFCEN_MASK 1
#define CCDC_DFCCTL_VDFCEN_MASK 1
#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4)
#define CCDC_DFCCTL_VDFCEN_SHIFT 4
#define CCDC_DFCCTL_VDFCSL_MASK 3
#define CCDC_DFCCTL_VDFCSL_SHIFT 5
#define CCDC_DFCCTL_VDFCUDA_MASK 1
#define CCDC_DFCCTL_VDFCUDA_SHIFT 7
#define CCDC_DFCCTL_VDFLSFT_MASK 3
#define CCDC_DFCCTL_VDFLSFT_SHIFT 8
#define CCDC_DFCMEMCTL_DFCMARST_MASK 1
#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2
#define CCDC_DFCMEMCTL_DFCMWR_MASK 1
#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0
#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2)
#define CCDC_LSCCFG_GFTSF_MASK 7
#define CCDC_LSCCFG_GFTSF_SHIFT 1
#define CCDC_LSCCFG_GFTINV_MASK 0xf
#define CCDC_LSCCFG_GFTINV_SHIFT 4
#define CCDC_LSC_GFTABLE_SEL_MASK 3
#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8
#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10
#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12
#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14
#define CCDC_LSC_GFMODE_MASK 3
#define CCDC_LSC_GFMODE_SHIFT 4
#define CCDC_LSC_DISABLE 0
#define CCDC_LSC_ENABLE 1
#define CCDC_LSC_TABLE1_SLC 0
#define CCDC_LSC_TABLE2_SLC 1
#define CCDC_LSC_TABLE3_SLC 2
#define CCDC_LSC_MEMADDR_RESET (1 << 2)
#define CCDC_LSC_MEMADDR_INCR (0 << 2)
#define CCDC_LSC_FRAC_MASK_T1 0xFF
#define CCDC_LSC_INT_MASK 3
#define CCDC_LSC_FRAC_MASK 0x3FFF
#define CCDC_LSC_CENTRE_MASK 0x3FFF
#define CCDC_LSC_COEF_MASK 0xff
#define CCDC_LSC_COEFL_SHIFT 0
#define CCDC_LSC_COEFU_SHIFT 8
#define CCDC_GAIN_MASK 0x7FF
#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0)
#define CCDC_SYNCEN_WEN_MASK (1 << 1)
#define CCDC_SYNCEN_WEN_SHIFT 1
/* Power on Defaults in hardware */
#define MODESET_DEFAULT 0x200
#define CULH_DEFAULT 0xFFFF
#define CULV_DEFAULT 0xFF
#define GAIN_DEFAULT 256
#define OUTCLIP_DEFAULT 0x3FFF
#define LSCCFG2_DEFAULT 0xE
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,153 @@
/*
* Copyright (C) 2006-2009 Texas Instruments Inc
*
* 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 _DM644X_CCDC_REGS_H
#define _DM644X_CCDC_REGS_H
/**************************************************************************\
* Register OFFSET Definitions
\**************************************************************************/
#define CCDC_PID 0x0
#define CCDC_PCR 0x4
#define CCDC_SYN_MODE 0x8
#define CCDC_HD_VD_WID 0xc
#define CCDC_PIX_LINES 0x10
#define CCDC_HORZ_INFO 0x14
#define CCDC_VERT_START 0x18
#define CCDC_VERT_LINES 0x1c
#define CCDC_CULLING 0x20
#define CCDC_HSIZE_OFF 0x24
#define CCDC_SDOFST 0x28
#define CCDC_SDR_ADDR 0x2c
#define CCDC_CLAMP 0x30
#define CCDC_DCSUB 0x34
#define CCDC_COLPTN 0x38
#define CCDC_BLKCMP 0x3c
#define CCDC_FPC 0x40
#define CCDC_FPC_ADDR 0x44
#define CCDC_VDINT 0x48
#define CCDC_ALAW 0x4c
#define CCDC_REC656IF 0x50
#define CCDC_CCDCFG 0x54
#define CCDC_FMTCFG 0x58
#define CCDC_FMT_HORZ 0x5c
#define CCDC_FMT_VERT 0x60
#define CCDC_FMT_ADDR0 0x64
#define CCDC_FMT_ADDR1 0x68
#define CCDC_FMT_ADDR2 0x6c
#define CCDC_FMT_ADDR3 0x70
#define CCDC_FMT_ADDR4 0x74
#define CCDC_FMT_ADDR5 0x78
#define CCDC_FMT_ADDR6 0x7c
#define CCDC_FMT_ADDR7 0x80
#define CCDC_PRGEVEN_0 0x84
#define CCDC_PRGEVEN_1 0x88
#define CCDC_PRGODD_0 0x8c
#define CCDC_PRGODD_1 0x90
#define CCDC_VP_OUT 0x94
#define CCDC_REG_END 0x98
/***************************************************************
* Define for various register bit mask and shifts for CCDC
****************************************************************/
#define CCDC_FID_POL_MASK 1
#define CCDC_FID_POL_SHIFT 4
#define CCDC_HD_POL_MASK 1
#define CCDC_HD_POL_SHIFT 3
#define CCDC_VD_POL_MASK 1
#define CCDC_VD_POL_SHIFT 2
#define CCDC_HSIZE_OFF_MASK 0xffffffe0
#define CCDC_32BYTE_ALIGN_VAL 31
#define CCDC_FRM_FMT_MASK 0x1
#define CCDC_FRM_FMT_SHIFT 7
#define CCDC_DATA_SZ_MASK 7
#define CCDC_DATA_SZ_SHIFT 8
#define CCDC_PIX_FMT_MASK 3
#define CCDC_PIX_FMT_SHIFT 12
#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF
#define CCDC_WEN_ENABLE (1 << 17)
#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF
#define CCDC_VDHDEN_ENABLE (1 << 16)
#define CCDC_LPF_ENABLE (1 << 14)
#define CCDC_ALAW_ENABLE (1 << 3)
#define CCDC_ALAW_GAMMA_WD_MASK 7
#define CCDC_BLK_CLAMP_ENABLE (1 << 31)
#define CCDC_BLK_SGAIN_MASK 0x1F
#define CCDC_BLK_ST_PXL_MASK 0x7FFF
#define CCDC_BLK_ST_PXL_SHIFT 10
#define CCDC_BLK_SAMPLE_LN_MASK 7
#define CCDC_BLK_SAMPLE_LN_SHIFT 28
#define CCDC_BLK_SAMPLE_LINE_MASK 7
#define CCDC_BLK_SAMPLE_LINE_SHIFT 25
#define CCDC_BLK_DC_SUB_MASK 0x03FFF
#define CCDC_BLK_COMP_MASK 0xFF
#define CCDC_BLK_COMP_GB_COMP_SHIFT 8
#define CCDC_BLK_COMP_GR_COMP_SHIFT 16
#define CCDC_BLK_COMP_R_COMP_SHIFT 24
#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15)
#define CCDC_FPC_ENABLE (1 << 15)
#define CCDC_FPC_DISABLE 0
#define CCDC_FPC_FPC_NUM_MASK 0x7FFF
#define CCDC_DATA_PACK_ENABLE (1 << 11)
#define CCDC_FMTCFG_VPIN_MASK 7
#define CCDC_FMTCFG_VPIN_SHIFT 12
#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF
#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF
#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16
#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF
#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF
#define CCDC_FMT_VERT_FMTSLV_SHIFT 16
#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF
#define CCDC_VP_OUT_VERT_NUM_SHIFT 17
#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF
#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4
#define CCDC_VP_OUT_HORZ_ST_MASK 0xF
#define CCDC_HORZ_INFO_SPH_SHIFT 16
#define CCDC_VERT_START_SLV0_SHIFT 16
#define CCDC_VDINT_VDINT0_SHIFT 16
#define CCDC_VDINT_VDINT1_MASK 0xFFFF
#define CCDC_PPC_RAW 1
#define CCDC_DCSUB_DEFAULT_VAL 0
#define CCDC_CLAMP_DEFAULT_VAL 0
#define CCDC_ENABLE_VIDEO_PORT 0x8000
#define CCDC_DISABLE_VIDEO_PORT 0
#define CCDC_COLPTN_VAL 0xBB11BB11
#define CCDC_TWO_BYTES_PER_PIXEL 2
#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D
#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249
#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000
#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0
#define CCDC_INTERLACED_HEIGHT_SHIFT 1
#define CCDC_SYN_MODE_INPMOD_SHIFT 12
#define CCDC_SYN_MODE_INPMOD_MASK 3
#define CCDC_SYN_MODE_8BITS (7 << 8)
#define CCDC_SYN_MODE_10BITS (6 << 8)
#define CCDC_SYN_MODE_11BITS (5 << 8)
#define CCDC_SYN_MODE_12BITS (4 << 8)
#define CCDC_SYN_MODE_13BITS (3 << 8)
#define CCDC_SYN_MODE_14BITS (2 << 8)
#define CCDC_SYN_MODE_15BITS (1 << 8)
#define CCDC_SYN_MODE_16BITS (0 << 8)
#define CCDC_SYN_FLDMODE_MASK 1
#define CCDC_SYN_FLDMODE_SHIFT 7
#define CCDC_REC656IF_BT656_EN 3
#define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2)
#define CCDC_CCDCFG_Y8POS_SHIFT 11
#define CCDC_CCDCFG_BW656_10BIT (1 << 5)
#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249
#define CCDC_NO_CULLING 0xffff00ff
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,269 @@
/*
* Copyright (C) 2008-2009 Texas Instruments Inc
*
* 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 _ISIF_REGS_H
#define _ISIF_REGS_H
/* ISIF registers relative offsets */
#define SYNCEN 0x00
#define MODESET 0x04
#define HDW 0x08
#define VDW 0x0c
#define PPLN 0x10
#define LPFR 0x14
#define SPH 0x18
#define LNH 0x1c
#define SLV0 0x20
#define SLV1 0x24
#define LNV 0x28
#define CULH 0x2c
#define CULV 0x30
#define HSIZE 0x34
#define SDOFST 0x38
#define CADU 0x3c
#define CADL 0x40
#define LINCFG0 0x44
#define LINCFG1 0x48
#define CCOLP 0x4c
#define CRGAIN 0x50
#define CGRGAIN 0x54
#define CGBGAIN 0x58
#define CBGAIN 0x5c
#define COFSTA 0x60
#define FLSHCFG0 0x64
#define FLSHCFG1 0x68
#define FLSHCFG2 0x6c
#define VDINT0 0x70
#define VDINT1 0x74
#define VDINT2 0x78
#define MISC 0x7c
#define CGAMMAWD 0x80
#define REC656IF 0x84
#define CCDCFG 0x88
/*****************************************************
* Defect Correction registers
*****************************************************/
#define DFCCTL 0x8c
#define VDFSATLV 0x90
#define DFCMEMCTL 0x94
#define DFCMEM0 0x98
#define DFCMEM1 0x9c
#define DFCMEM2 0xa0
#define DFCMEM3 0xa4
#define DFCMEM4 0xa8
/****************************************************
* Black Clamp registers
****************************************************/
#define CLAMPCFG 0xac
#define CLDCOFST 0xb0
#define CLSV 0xb4
#define CLHWIN0 0xb8
#define CLHWIN1 0xbc
#define CLHWIN2 0xc0
#define CLVRV 0xc4
#define CLVWIN0 0xc8
#define CLVWIN1 0xcc
#define CLVWIN2 0xd0
#define CLVWIN3 0xd4
/****************************************************
* Lense Shading Correction
****************************************************/
#define DATAHOFST 0xd8
#define DATAVOFST 0xdc
#define LSCHVAL 0xe0
#define LSCVVAL 0xe4
#define TWODLSCCFG 0xe8
#define TWODLSCOFST 0xec
#define TWODLSCINI 0xf0
#define TWODLSCGRBU 0xf4
#define TWODLSCGRBL 0xf8
#define TWODLSCGROF 0xfc
#define TWODLSCORBU 0x100
#define TWODLSCORBL 0x104
#define TWODLSCOROF 0x108
#define TWODLSCIRQEN 0x10c
#define TWODLSCIRQST 0x110
/****************************************************
* Data formatter
****************************************************/
#define FMTCFG 0x114
#define FMTPLEN 0x118
#define FMTSPH 0x11c
#define FMTLNH 0x120
#define FMTSLV 0x124
#define FMTLNV 0x128
#define FMTRLEN 0x12c
#define FMTHCNT 0x130
#define FMTAPTR_BASE 0x134
/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */
#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4))
#define FMTPGMVF0 0x174
#define FMTPGMVF1 0x178
#define FMTPGMAPU0 0x17c
#define FMTPGMAPU1 0x180
#define FMTPGMAPS0 0x184
#define FMTPGMAPS1 0x188
#define FMTPGMAPS2 0x18c
#define FMTPGMAPS3 0x190
#define FMTPGMAPS4 0x194
#define FMTPGMAPS5 0x198
#define FMTPGMAPS6 0x19c
#define FMTPGMAPS7 0x1a0
/************************************************
* Color Space Converter
************************************************/
#define CSCCTL 0x1a4
#define CSCM0 0x1a8
#define CSCM1 0x1ac
#define CSCM2 0x1b0
#define CSCM3 0x1b4
#define CSCM4 0x1b8
#define CSCM5 0x1bc
#define CSCM6 0x1c0
#define CSCM7 0x1c4
#define OBWIN0 0x1c8
#define OBWIN1 0x1cc
#define OBWIN2 0x1d0
#define OBWIN3 0x1d4
#define OBVAL0 0x1d8
#define OBVAL1 0x1dc
#define OBVAL2 0x1e0
#define OBVAL3 0x1e4
#define OBVAL4 0x1e8
#define OBVAL5 0x1ec
#define OBVAL6 0x1f0
#define OBVAL7 0x1f4
#define CLKCTL 0x1f8
/* Masks & Shifts below */
#define START_PX_HOR_MASK 0x7FFF
#define NUM_PX_HOR_MASK 0x7FFF
#define START_VER_ONE_MASK 0x7FFF
#define START_VER_TWO_MASK 0x7FFF
#define NUM_LINES_VER 0x7FFF
/* gain - offset masks */
#define GAIN_INTEGER_SHIFT 9
#define OFFSET_MASK 0xFFF
#define GAIN_SDRAM_EN_SHIFT 12
#define GAIN_IPIPE_EN_SHIFT 13
#define GAIN_H3A_EN_SHIFT 14
#define OFST_SDRAM_EN_SHIFT 8
#define OFST_IPIPE_EN_SHIFT 9
#define OFST_H3A_EN_SHIFT 10
#define GAIN_OFFSET_EN_MASK 0x7700
/* Culling */
#define CULL_PAT_EVEN_LINE_SHIFT 8
/* CCDCFG register */
#define ISIF_YCINSWP_RAW (0x00 << 4)
#define ISIF_YCINSWP_YCBCR (0x01 << 4)
#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6)
#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8)
#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9)
#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10)
#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15)
#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15)
#define ISIF_DATA_PACK_MASK 3
#define ISIF_DATA_PACK16 0
#define ISIF_DATA_PACK12 1
#define ISIF_DATA_PACK8 2
#define ISIF_PIX_ORDER_SHIFT 11
#define ISIF_BW656_ENABLE (0x01 << 5)
/* MODESET registers */
#define ISIF_VDHDOUT_INPUT (0x00 << 0)
#define ISIF_INPUT_SHIFT 12
#define ISIF_RAW_INPUT_MODE 0
#define ISIF_FID_POL_SHIFT 4
#define ISIF_HD_POL_SHIFT 3
#define ISIF_VD_POL_SHIFT 2
#define ISIF_DATAPOL_NORMAL 0
#define ISIF_DATAPOL_SHIFT 6
#define ISIF_EXWEN_DISABLE 0
#define ISIF_EXWEN_SHIFT 5
#define ISIF_FRM_FMT_SHIFT 7
#define ISIF_DATASFT_SHIFT 8
#define ISIF_LPF_SHIFT 14
#define ISIF_LPF_MASK 1
/* GAMMAWD registers */
#define ISIF_ALAW_GAMMA_WD_MASK 0xF
#define ISIF_ALAW_GAMMA_WD_SHIFT 1
#define ISIF_ALAW_ENABLE 1
#define ISIF_GAMMAWD_CFA_SHIFT 5
/* HSIZE registers */
#define ISIF_HSIZE_FLIP_MASK 1
#define ISIF_HSIZE_FLIP_SHIFT 12
/* MISC registers */
#define ISIF_DPCM_EN_SHIFT 12
#define ISIF_DPCM_PREDICTOR_SHIFT 13
/* Black clamp related */
#define ISIF_BC_MODE_COLOR_SHIFT 4
#define ISIF_HORZ_BC_MODE_SHIFT 1
#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5
#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6
#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8
#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12
#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4
#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8
/* VDFC registers */
#define ISIF_VDFC_EN_SHIFT 4
#define ISIF_VDFC_CORR_MOD_SHIFT 5
#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7
#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8
#define ISIF_VDFC_POS_MASK 0x1FFF
#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2
/* CSC registers */
#define ISIF_CSC_COEF_INTEG_MASK 7
#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f
#define ISIF_CSC_COEF_INTEG_SHIFT 5
#define ISIF_CSCM_MSB_SHIFT 8
#define ISIF_DF_CSC_SPH_MASK 0x1FFF
#define ISIF_DF_CSC_LNH_MASK 0x1FFF
#define ISIF_DF_CSC_SLV_MASK 0x1FFF
#define ISIF_DF_CSC_LNV_MASK 0x1FFF
#define ISIF_DF_NUMLINES 0x7FFF
#define ISIF_DF_NUMPIX 0x1FFF
/* Offsets for LSC/DFC/Gain */
#define ISIF_DATA_H_OFFSET_MASK 0x1FFF
#define ISIF_DATA_V_OFFSET_MASK 0x1FFF
/* Linearization */
#define ISIF_LIN_CORRSFT_SHIFT 4
#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10
/* Pattern registers */
#define ISIF_PG_EN (1 << 3)
#define ISIF_SEL_PG_SRC (3 << 4)
#define ISIF_PG_VD_POL_SHIFT 0
#define ISIF_PG_HD_POL_SHIFT 1
/*random other junk*/
#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0)
#define ISIF_SYNCEN_WEN_MASK (1 << 1)
#define ISIF_SYNCEN_WEN_SHIFT 1
#endif

View file

@ -0,0 +1,872 @@
/*
* Copyright (C) 2010 Texas Instruments Inc
*
* 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 version 2.
*
* 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/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <media/v4l2-device.h>
#include <media/davinci/vpbe_types.h>
#include <media/davinci/vpbe.h>
#include <media/davinci/vpss.h>
#include <media/davinci/vpbe_venc.h>
#define VPBE_DEFAULT_OUTPUT "Composite"
#define VPBE_DEFAULT_MODE "ntsc"
static char *def_output = VPBE_DEFAULT_OUTPUT;
static char *def_mode = VPBE_DEFAULT_MODE;
static int debug;
module_param(def_output, charp, S_IRUGO);
module_param(def_mode, charp, S_IRUGO);
module_param(debug, int, 0644);
MODULE_PARM_DESC(def_output, "vpbe output name (default:Composite)");
MODULE_PARM_DESC(def_mode, "vpbe output mode name (default:ntsc");
MODULE_PARM_DESC(debug, "Debug level 0-1");
MODULE_DESCRIPTION("TI DMXXX VPBE Display controller");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Texas Instruments");
/**
* vpbe_current_encoder_info - Get config info for current encoder
* @vpbe_dev - vpbe device ptr
*
* Return ptr to current encoder config info
*/
static struct encoder_config_info*
vpbe_current_encoder_info(struct vpbe_device *vpbe_dev)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
int index = vpbe_dev->current_sd_index;
return ((index == 0) ? &cfg->venc :
&cfg->ext_encoders[index-1]);
}
/**
* vpbe_find_encoder_sd_index - Given a name find encoder sd index
*
* @vpbe_config - ptr to vpbe cfg
* @output_index - index used by application
*
* Return sd index of the encoder
*/
static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg,
int index)
{
char *encoder_name = cfg->outputs[index].subdev_name;
int i;
/* Venc is always first */
if (!strcmp(encoder_name, cfg->venc.module_name))
return 0;
for (i = 0; i < cfg->num_ext_encoders; i++) {
if (!strcmp(encoder_name,
cfg->ext_encoders[i].module_name))
return i+1;
}
return -EINVAL;
}
/**
* vpbe_g_cropcap - Get crop capabilities of the display
* @vpbe_dev - vpbe device ptr
* @cropcap - cropcap is a ptr to struct v4l2_cropcap
*
* Update the crop capabilities in crop cap for current
* mode
*/
static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev,
struct v4l2_cropcap *cropcap)
{
if (NULL == cropcap)
return -EINVAL;
cropcap->bounds.left = 0;
cropcap->bounds.top = 0;
cropcap->bounds.width = vpbe_dev->current_timings.xres;
cropcap->bounds.height = vpbe_dev->current_timings.yres;
cropcap->defrect = cropcap->bounds;
return 0;
}
/**
* vpbe_enum_outputs - enumerate outputs
* @vpbe_dev - vpbe device ptr
* @output - ptr to v4l2_output structure
*
* Enumerates the outputs available at the vpbe display
* returns the status, -EINVAL if end of output list
*/
static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev,
struct v4l2_output *output)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
int temp_index = output->index;
if (temp_index >= cfg->num_outputs)
return -EINVAL;
*output = cfg->outputs[temp_index].output;
output->index = temp_index;
return 0;
}
static int vpbe_get_mode_info(struct vpbe_device *vpbe_dev, char *mode,
int output_index)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
struct vpbe_enc_mode_info var;
int curr_output = output_index;
int i;
if (NULL == mode)
return -EINVAL;
for (i = 0; i < cfg->outputs[curr_output].num_modes; i++) {
var = cfg->outputs[curr_output].modes[i];
if (!strcmp(mode, var.name)) {
vpbe_dev->current_timings = var;
return 0;
}
}
return -EINVAL;
}
static int vpbe_get_current_mode_info(struct vpbe_device *vpbe_dev,
struct vpbe_enc_mode_info *mode_info)
{
if (NULL == mode_info)
return -EINVAL;
*mode_info = vpbe_dev->current_timings;
return 0;
}
/* Get std by std id */
static int vpbe_get_std_info(struct vpbe_device *vpbe_dev,
v4l2_std_id std_id)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
struct vpbe_enc_mode_info var;
int curr_output = vpbe_dev->current_out_index;
int i;
for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
var = cfg->outputs[curr_output].modes[i];
if ((var.timings_type & VPBE_ENC_STD) &&
(var.std_id & std_id)) {
vpbe_dev->current_timings = var;
return 0;
}
}
return -EINVAL;
}
static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev,
char *std_name)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
struct vpbe_enc_mode_info var;
int curr_output = vpbe_dev->current_out_index;
int i;
for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
var = cfg->outputs[curr_output].modes[i];
if (!strcmp(var.name, std_name)) {
vpbe_dev->current_timings = var;
return 0;
}
}
return -EINVAL;
}
/**
* vpbe_set_output - Set output
* @vpbe_dev - vpbe device ptr
* @index - index of output
*
* Set vpbe output to the output specified by the index
*/
static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index)
{
struct encoder_config_info *curr_enc_info =
vpbe_current_encoder_info(vpbe_dev);
struct vpbe_config *cfg = vpbe_dev->cfg;
struct venc_platform_data *venc_device = vpbe_dev->venc_device;
enum v4l2_mbus_pixelcode if_params;
int enc_out_index;
int sd_index;
int ret = 0;
if (index >= cfg->num_outputs)
return -EINVAL;
mutex_lock(&vpbe_dev->lock);
sd_index = vpbe_dev->current_sd_index;
enc_out_index = cfg->outputs[index].output.index;
/*
* Currently we switch the encoder based on output selected
* by the application. If media controller is implemented later
* there is will be an API added to setup_link between venc
* and external encoder. So in that case below comparison always
* match and encoder will not be switched. But if application
* chose not to use media controller, then this provides current
* way of switching encoder at the venc output.
*/
if (strcmp(curr_enc_info->module_name,
cfg->outputs[index].subdev_name)) {
/* Need to switch the encoder at the output */
sd_index = vpbe_find_encoder_sd_index(cfg, index);
if (sd_index < 0) {
ret = -EINVAL;
goto out;
}
if_params = cfg->outputs[index].if_params;
venc_device->setup_if_config(if_params);
if (ret)
goto out;
}
/* Set output at the encoder */
ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
s_routing, 0, enc_out_index, 0);
if (ret)
goto out;
/*
* It is assumed that venc or extenal encoder will set a default
* mode in the sub device. For external encoder or LCD pannel output,
* we also need to set up the lcd port for the required mode. So setup
* the lcd port for the default mode that is configured in the board
* arch/arm/mach-davinci/board-dm355-evm.setup file for the external
* encoder.
*/
ret = vpbe_get_mode_info(vpbe_dev,
cfg->outputs[index].default_mode, index);
if (!ret) {
struct osd_state *osd_device = vpbe_dev->osd_device;
osd_device->ops.set_left_margin(osd_device,
vpbe_dev->current_timings.left_margin);
osd_device->ops.set_top_margin(osd_device,
vpbe_dev->current_timings.upper_margin);
vpbe_dev->current_sd_index = sd_index;
vpbe_dev->current_out_index = index;
}
out:
mutex_unlock(&vpbe_dev->lock);
return ret;
}
static int vpbe_set_default_output(struct vpbe_device *vpbe_dev)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
int ret = 0;
int i;
for (i = 0; i < cfg->num_outputs; i++) {
if (!strcmp(def_output,
cfg->outputs[i].output.name)) {
ret = vpbe_set_output(vpbe_dev, i);
if (!ret)
vpbe_dev->current_out_index = i;
return ret;
}
}
return ret;
}
/**
* vpbe_get_output - Get output
* @vpbe_dev - vpbe device ptr
*
* return current vpbe output to the the index
*/
static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev)
{
return vpbe_dev->current_out_index;
}
/**
* vpbe_s_dv_timings - Set the given preset timings in the encoder
*
* Sets the timings if supported by the current encoder. Return the status.
* 0 - success & -EINVAL on error
*/
static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev,
struct v4l2_dv_timings *dv_timings)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
int out_index = vpbe_dev->current_out_index;
struct vpbe_output *output = &cfg->outputs[out_index];
int sd_index = vpbe_dev->current_sd_index;
int ret, i;
if (!(cfg->outputs[out_index].output.capabilities &
V4L2_OUT_CAP_DV_TIMINGS))
return -EINVAL;
for (i = 0; i < output->num_modes; i++) {
if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS &&
!memcmp(&output->modes[i].dv_timings,
dv_timings, sizeof(*dv_timings)))
break;
}
if (i >= output->num_modes)
return -EINVAL;
vpbe_dev->current_timings = output->modes[i];
mutex_lock(&vpbe_dev->lock);
ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
s_dv_timings, dv_timings);
if (!ret && (vpbe_dev->amp != NULL)) {
/* Call amplifier subdevice */
ret = v4l2_subdev_call(vpbe_dev->amp, video,
s_dv_timings, dv_timings);
}
/* set the lcd controller output for the given mode */
if (!ret) {
struct osd_state *osd_device = vpbe_dev->osd_device;
osd_device->ops.set_left_margin(osd_device,
vpbe_dev->current_timings.left_margin);
osd_device->ops.set_top_margin(osd_device,
vpbe_dev->current_timings.upper_margin);
}
mutex_unlock(&vpbe_dev->lock);
return ret;
}
/**
* vpbe_g_dv_timings - Get the timings in the current encoder
*
* Get the timings in the current encoder. Return the status. 0 - success
* -EINVAL on error
*/
static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev,
struct v4l2_dv_timings *dv_timings)
{
if (vpbe_dev->current_timings.timings_type &
VPBE_ENC_DV_TIMINGS) {
*dv_timings = vpbe_dev->current_timings.dv_timings;
return 0;
}
return -EINVAL;
}
/**
* vpbe_enum_dv_timings - Enumerate the dv timings in the current encoder
*
* Get the timings in the current encoder. Return the status. 0 - success
* -EINVAL on error
*/
static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev,
struct v4l2_enum_dv_timings *timings)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
int out_index = vpbe_dev->current_out_index;
struct vpbe_output *output = &cfg->outputs[out_index];
int j = 0;
int i;
if (!(output->output.capabilities & V4L2_OUT_CAP_DV_TIMINGS))
return -EINVAL;
for (i = 0; i < output->num_modes; i++) {
if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS) {
if (j == timings->index)
break;
j++;
}
}
if (i == output->num_modes)
return -EINVAL;
timings->timings = output->modes[i].dv_timings;
return 0;
}
/**
* vpbe_s_std - Set the given standard in the encoder
*
* Sets the standard if supported by the current encoder. Return the status.
* 0 - success & -EINVAL on error
*/
static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id std_id)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
int out_index = vpbe_dev->current_out_index;
int sd_index = vpbe_dev->current_sd_index;
int ret;
if (!(cfg->outputs[out_index].output.capabilities &
V4L2_OUT_CAP_STD))
return -EINVAL;
ret = vpbe_get_std_info(vpbe_dev, std_id);
if (ret)
return ret;
mutex_lock(&vpbe_dev->lock);
ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
s_std_output, std_id);
/* set the lcd controller output for the given mode */
if (!ret) {
struct osd_state *osd_device = vpbe_dev->osd_device;
osd_device->ops.set_left_margin(osd_device,
vpbe_dev->current_timings.left_margin);
osd_device->ops.set_top_margin(osd_device,
vpbe_dev->current_timings.upper_margin);
}
mutex_unlock(&vpbe_dev->lock);
return ret;
}
/**
* vpbe_g_std - Get the standard in the current encoder
*
* Get the standard in the current encoder. Return the status. 0 - success
* -EINVAL on error
*/
static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
{
struct vpbe_enc_mode_info *cur_timings = &vpbe_dev->current_timings;
if (cur_timings->timings_type & VPBE_ENC_STD) {
*std_id = cur_timings->std_id;
return 0;
}
return -EINVAL;
}
/**
* vpbe_set_mode - Set mode in the current encoder using mode info
*
* Use the mode string to decide what timings to set in the encoder
* This is typically useful when fbset command is used to change the current
* timings by specifying a string to indicate the timings.
*/
static int vpbe_set_mode(struct vpbe_device *vpbe_dev,
struct vpbe_enc_mode_info *mode_info)
{
struct vpbe_enc_mode_info *preset_mode = NULL;
struct vpbe_config *cfg = vpbe_dev->cfg;
struct v4l2_dv_timings dv_timings;
struct osd_state *osd_device;
int out_index = vpbe_dev->current_out_index;
int ret = 0;
int i;
if ((NULL == mode_info) || (NULL == mode_info->name))
return -EINVAL;
for (i = 0; i < cfg->outputs[out_index].num_modes; i++) {
if (!strcmp(mode_info->name,
cfg->outputs[out_index].modes[i].name)) {
preset_mode = &cfg->outputs[out_index].modes[i];
/*
* it may be one of the 3 timings type. Check and
* invoke right API
*/
if (preset_mode->timings_type & VPBE_ENC_STD)
return vpbe_s_std(vpbe_dev,
preset_mode->std_id);
if (preset_mode->timings_type &
VPBE_ENC_DV_TIMINGS) {
dv_timings =
preset_mode->dv_timings;
return vpbe_s_dv_timings(vpbe_dev, &dv_timings);
}
}
}
/* Only custom timing should reach here */
if (preset_mode == NULL)
return -EINVAL;
mutex_lock(&vpbe_dev->lock);
osd_device = vpbe_dev->osd_device;
vpbe_dev->current_timings = *preset_mode;
osd_device->ops.set_left_margin(osd_device,
vpbe_dev->current_timings.left_margin);
osd_device->ops.set_top_margin(osd_device,
vpbe_dev->current_timings.upper_margin);
mutex_unlock(&vpbe_dev->lock);
return ret;
}
static int vpbe_set_default_mode(struct vpbe_device *vpbe_dev)
{
int ret;
ret = vpbe_get_std_info_by_name(vpbe_dev, def_mode);
if (ret)
return ret;
/* set the default mode in the encoder */
return vpbe_set_mode(vpbe_dev, &vpbe_dev->current_timings);
}
static int platform_device_get(struct device *dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct vpbe_device *vpbe_dev = data;
if (strstr(pdev->name, "vpbe-osd") != NULL)
vpbe_dev->osd_device = platform_get_drvdata(pdev);
if (strstr(pdev->name, "vpbe-venc") != NULL)
vpbe_dev->venc_device = dev_get_platdata(&pdev->dev);
return 0;
}
/**
* vpbe_initialize() - Initialize the vpbe display controller
* @vpbe_dev - vpbe device ptr
*
* Master frame buffer device drivers calls this to initialize vpbe
* display controller. This will then registers v4l2 device and the sub
* devices and sets a current encoder sub device for display. v4l2 display
* device driver is the master and frame buffer display device driver is
* the slave. Frame buffer display driver checks the initialized during
* probe and exit if not initialized. Returns status.
*/
static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
{
struct encoder_config_info *enc_info;
struct amp_config_info *amp_info;
struct v4l2_subdev **enc_subdev;
struct osd_state *osd_device;
struct i2c_adapter *i2c_adap;
int num_encoders;
int ret = 0;
int err;
int i;
/*
* v4l2 abd FBDev frame buffer devices will get the vpbe_dev pointer
* from the platform device by iteration of platform drivers and
* matching with device name
*/
if (NULL == vpbe_dev || NULL == dev) {
printk(KERN_ERR "Null device pointers.\n");
return -ENODEV;
}
if (vpbe_dev->initialized)
return 0;
mutex_lock(&vpbe_dev->lock);
if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
/* We have dac clock available for platform */
vpbe_dev->dac_clk = clk_get(vpbe_dev->pdev, "vpss_dac");
if (IS_ERR(vpbe_dev->dac_clk)) {
ret = PTR_ERR(vpbe_dev->dac_clk);
goto fail_mutex_unlock;
}
if (clk_prepare_enable(vpbe_dev->dac_clk)) {
ret = -ENODEV;
goto fail_mutex_unlock;
}
}
/* first enable vpss clocks */
vpss_enable_clock(VPSS_VPBE_CLOCK, 1);
/* First register a v4l2 device */
ret = v4l2_device_register(dev, &vpbe_dev->v4l2_dev);
if (ret) {
v4l2_err(dev->driver,
"Unable to register v4l2 device.\n");
goto fail_clk_put;
}
v4l2_info(&vpbe_dev->v4l2_dev, "vpbe v4l2 device registered\n");
err = bus_for_each_dev(&platform_bus_type, NULL, vpbe_dev,
platform_device_get);
if (err < 0) {
ret = err;
goto fail_dev_unregister;
}
vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev,
vpbe_dev->cfg->venc.module_name);
/* register venc sub device */
if (vpbe_dev->venc == NULL) {
v4l2_err(&vpbe_dev->v4l2_dev,
"vpbe unable to init venc sub device\n");
ret = -ENODEV;
goto fail_dev_unregister;
}
/* initialize osd device */
osd_device = vpbe_dev->osd_device;
if (NULL != osd_device->ops.initialize) {
err = osd_device->ops.initialize(osd_device);
if (err) {
v4l2_err(&vpbe_dev->v4l2_dev,
"unable to initialize the OSD device");
err = -ENOMEM;
goto fail_dev_unregister;
}
}
/*
* Register any external encoders that are configured. At index 0 we
* store venc sd index.
*/
num_encoders = vpbe_dev->cfg->num_ext_encoders + 1;
vpbe_dev->encoders = kmalloc(
sizeof(struct v4l2_subdev *)*num_encoders,
GFP_KERNEL);
if (NULL == vpbe_dev->encoders) {
v4l2_err(&vpbe_dev->v4l2_dev,
"unable to allocate memory for encoders sub devices");
ret = -ENOMEM;
goto fail_dev_unregister;
}
i2c_adap = i2c_get_adapter(vpbe_dev->cfg->i2c_adapter_id);
for (i = 0; i < (vpbe_dev->cfg->num_ext_encoders + 1); i++) {
if (i == 0) {
/* venc is at index 0 */
enc_subdev = &vpbe_dev->encoders[i];
*enc_subdev = vpbe_dev->venc;
continue;
}
enc_info = &vpbe_dev->cfg->ext_encoders[i];
if (enc_info->is_i2c) {
enc_subdev = &vpbe_dev->encoders[i];
*enc_subdev = v4l2_i2c_new_subdev_board(
&vpbe_dev->v4l2_dev, i2c_adap,
&enc_info->board_info, NULL);
if (*enc_subdev)
v4l2_info(&vpbe_dev->v4l2_dev,
"v4l2 sub device %s registered\n",
enc_info->module_name);
else {
v4l2_err(&vpbe_dev->v4l2_dev, "encoder %s"
" failed to register",
enc_info->module_name);
ret = -ENODEV;
goto fail_kfree_encoders;
}
} else
v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c encoders"
" currently not supported");
}
/* Add amplifier subdevice for dm365 */
if ((strcmp(vpbe_dev->cfg->module_name, "dm365-vpbe-display") == 0) &&
vpbe_dev->cfg->amp != NULL) {
amp_info = vpbe_dev->cfg->amp;
if (amp_info->is_i2c) {
vpbe_dev->amp = v4l2_i2c_new_subdev_board(
&vpbe_dev->v4l2_dev, i2c_adap,
&amp_info->board_info, NULL);
if (!vpbe_dev->amp) {
v4l2_err(&vpbe_dev->v4l2_dev,
"amplifier %s failed to register",
amp_info->module_name);
ret = -ENODEV;
goto fail_kfree_encoders;
}
v4l2_info(&vpbe_dev->v4l2_dev,
"v4l2 sub device %s registered\n",
amp_info->module_name);
} else {
vpbe_dev->amp = NULL;
v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c amplifiers"
" currently not supported");
}
} else {
vpbe_dev->amp = NULL;
}
/* set the current encoder and output to that of venc by default */
vpbe_dev->current_sd_index = 0;
vpbe_dev->current_out_index = 0;
mutex_unlock(&vpbe_dev->lock);
printk(KERN_NOTICE "Setting default output to %s\n", def_output);
ret = vpbe_set_default_output(vpbe_dev);
if (ret) {
v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default output %s",
def_output);
return ret;
}
printk(KERN_NOTICE "Setting default mode to %s\n", def_mode);
ret = vpbe_set_default_mode(vpbe_dev);
if (ret) {
v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default mode %s",
def_mode);
return ret;
}
vpbe_dev->initialized = 1;
/* TBD handling of bootargs for default output and mode */
return 0;
fail_kfree_encoders:
kfree(vpbe_dev->encoders);
fail_dev_unregister:
v4l2_device_unregister(&vpbe_dev->v4l2_dev);
fail_clk_put:
if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
clk_disable_unprepare(vpbe_dev->dac_clk);
clk_put(vpbe_dev->dac_clk);
}
fail_mutex_unlock:
mutex_unlock(&vpbe_dev->lock);
return ret;
}
/**
* vpbe_deinitialize() - de-initialize the vpbe display controller
* @dev - Master and slave device ptr
*
* vpbe_master and slave frame buffer devices calls this to de-initialize
* the display controller. It is called when master and slave device
* driver modules are removed and no longer requires the display controller.
*/
static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev)
{
v4l2_device_unregister(&vpbe_dev->v4l2_dev);
if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
clk_disable_unprepare(vpbe_dev->dac_clk);
clk_put(vpbe_dev->dac_clk);
}
kfree(vpbe_dev->amp);
kfree(vpbe_dev->encoders);
vpbe_dev->initialized = 0;
/* disable vpss clocks */
vpss_enable_clock(VPSS_VPBE_CLOCK, 0);
}
static struct vpbe_device_ops vpbe_dev_ops = {
.g_cropcap = vpbe_g_cropcap,
.enum_outputs = vpbe_enum_outputs,
.set_output = vpbe_set_output,
.get_output = vpbe_get_output,
.s_dv_timings = vpbe_s_dv_timings,
.g_dv_timings = vpbe_g_dv_timings,
.enum_dv_timings = vpbe_enum_dv_timings,
.s_std = vpbe_s_std,
.g_std = vpbe_g_std,
.initialize = vpbe_initialize,
.deinitialize = vpbe_deinitialize,
.get_mode_info = vpbe_get_current_mode_info,
.set_mode = vpbe_set_mode,
};
static int vpbe_probe(struct platform_device *pdev)
{
struct vpbe_device *vpbe_dev;
struct vpbe_config *cfg;
int ret = -EINVAL;
if (pdev->dev.platform_data == NULL) {
v4l2_err(pdev->dev.driver, "No platform data\n");
return -ENODEV;
}
cfg = pdev->dev.platform_data;
if (!cfg->module_name[0] ||
!cfg->osd.module_name[0] ||
!cfg->venc.module_name[0]) {
v4l2_err(pdev->dev.driver, "vpbe display module names not"
" defined\n");
return ret;
}
vpbe_dev = kzalloc(sizeof(*vpbe_dev), GFP_KERNEL);
if (vpbe_dev == NULL) {
v4l2_err(pdev->dev.driver, "Unable to allocate memory"
" for vpbe_device\n");
return -ENOMEM;
}
vpbe_dev->cfg = cfg;
vpbe_dev->ops = vpbe_dev_ops;
vpbe_dev->pdev = &pdev->dev;
if (cfg->outputs->num_modes > 0)
vpbe_dev->current_timings = vpbe_dev->cfg->outputs[0].modes[0];
else {
kfree(vpbe_dev);
return -ENODEV;
}
/* set the driver data in platform device */
platform_set_drvdata(pdev, vpbe_dev);
mutex_init(&vpbe_dev->lock);
return 0;
}
static int vpbe_remove(struct platform_device *device)
{
struct vpbe_device *vpbe_dev = platform_get_drvdata(device);
kfree(vpbe_dev);
return 0;
}
static struct platform_driver vpbe_driver = {
.driver = {
.name = "vpbe_controller",
.owner = THIS_MODULE,
},
.probe = vpbe_probe,
.remove = vpbe_remove,
};
module_platform_driver(vpbe_driver);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,364 @@
/*
* Copyright (C) 2006-2010 Texas Instruments Inc
*
* 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 version 2.
*
* 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 _VPBE_OSD_REGS_H
#define _VPBE_OSD_REGS_H
/* VPBE Global Registers */
#define VPBE_PID 0x0
#define VPBE_PCR 0x4
/* VPSS CLock Registers */
#define VPSSCLK_PID 0x00
#define VPSSCLK_CLKCTRL 0x04
/* VPSS Buffer Logic Registers */
#define VPSSBL_PID 0x00
#define VPSSBL_PCR 0x04
#define VPSSBL_BCR 0x08
#define VPSSBL_INTSTAT 0x0C
#define VPSSBL_INTSEL 0x10
#define VPSSBL_EVTSEL 0x14
#define VPSSBL_MEMCTRL 0x18
#define VPSSBL_CCDCMUX 0x1C
/* DM365 ISP5 system configuration */
#define ISP5_PID 0x0
#define ISP5_PCCR 0x4
#define ISP5_BCR 0x8
#define ISP5_INTSTAT 0xC
#define ISP5_INTSEL1 0x10
#define ISP5_INTSEL2 0x14
#define ISP5_INTSEL3 0x18
#define ISP5_EVTSEL 0x1c
#define ISP5_CCDCMUX 0x20
/* VPBE On-Screen Display Subsystem Registers (OSD) */
#define OSD_MODE 0x00
#define OSD_VIDWINMD 0x04
#define OSD_OSDWIN0MD 0x08
#define OSD_OSDWIN1MD 0x0C
#define OSD_OSDATRMD 0x0C
#define OSD_RECTCUR 0x10
#define OSD_VIDWIN0OFST 0x18
#define OSD_VIDWIN1OFST 0x1C
#define OSD_OSDWIN0OFST 0x20
#define OSD_OSDWIN1OFST 0x24
#define OSD_VIDWINADH 0x28
#define OSD_VIDWIN0ADL 0x2C
#define OSD_VIDWIN0ADR 0x2C
#define OSD_VIDWIN1ADL 0x30
#define OSD_VIDWIN1ADR 0x30
#define OSD_OSDWINADH 0x34
#define OSD_OSDWIN0ADL 0x38
#define OSD_OSDWIN0ADR 0x38
#define OSD_OSDWIN1ADL 0x3C
#define OSD_OSDWIN1ADR 0x3C
#define OSD_BASEPX 0x40
#define OSD_BASEPY 0x44
#define OSD_VIDWIN0XP 0x48
#define OSD_VIDWIN0YP 0x4C
#define OSD_VIDWIN0XL 0x50
#define OSD_VIDWIN0YL 0x54
#define OSD_VIDWIN1XP 0x58
#define OSD_VIDWIN1YP 0x5C
#define OSD_VIDWIN1XL 0x60
#define OSD_VIDWIN1YL 0x64
#define OSD_OSDWIN0XP 0x68
#define OSD_OSDWIN0YP 0x6C
#define OSD_OSDWIN0XL 0x70
#define OSD_OSDWIN0YL 0x74
#define OSD_OSDWIN1XP 0x78
#define OSD_OSDWIN1YP 0x7C
#define OSD_OSDWIN1XL 0x80
#define OSD_OSDWIN1YL 0x84
#define OSD_CURXP 0x88
#define OSD_CURYP 0x8C
#define OSD_CURXL 0x90
#define OSD_CURYL 0x94
#define OSD_W0BMP01 0xA0
#define OSD_W0BMP23 0xA4
#define OSD_W0BMP45 0xA8
#define OSD_W0BMP67 0xAC
#define OSD_W0BMP89 0xB0
#define OSD_W0BMPAB 0xB4
#define OSD_W0BMPCD 0xB8
#define OSD_W0BMPEF 0xBC
#define OSD_W1BMP01 0xC0
#define OSD_W1BMP23 0xC4
#define OSD_W1BMP45 0xC8
#define OSD_W1BMP67 0xCC
#define OSD_W1BMP89 0xD0
#define OSD_W1BMPAB 0xD4
#define OSD_W1BMPCD 0xD8
#define OSD_W1BMPEF 0xDC
#define OSD_VBNDRY 0xE0
#define OSD_EXTMODE 0xE4
#define OSD_MISCCTL 0xE8
#define OSD_CLUTRAMYCB 0xEC
#define OSD_CLUTRAMCR 0xF0
#define OSD_TRANSPVAL 0xF4
#define OSD_TRANSPVALL 0xF4
#define OSD_TRANSPVALU 0xF8
#define OSD_TRANSPBMPIDX 0xFC
#define OSD_PPVWIN0ADR 0xFC
/* bit definitions */
#define VPBE_PCR_VENC_DIV (1 << 1)
#define VPBE_PCR_CLK_OFF (1 << 0)
#define VPSSBL_INTSTAT_HSSIINT (1 << 14)
#define VPSSBL_INTSTAT_CFALDINT (1 << 13)
#define VPSSBL_INTSTAT_IPIPE_INT5 (1 << 12)
#define VPSSBL_INTSTAT_IPIPE_INT4 (1 << 11)
#define VPSSBL_INTSTAT_IPIPE_INT3 (1 << 10)
#define VPSSBL_INTSTAT_IPIPE_INT2 (1 << 9)
#define VPSSBL_INTSTAT_IPIPE_INT1 (1 << 8)
#define VPSSBL_INTSTAT_IPIPE_INT0 (1 << 7)
#define VPSSBL_INTSTAT_IPIPEIFINT (1 << 6)
#define VPSSBL_INTSTAT_OSDINT (1 << 5)
#define VPSSBL_INTSTAT_VENCINT (1 << 4)
#define VPSSBL_INTSTAT_H3AINT (1 << 3)
#define VPSSBL_INTSTAT_CCDC_VDINT2 (1 << 2)
#define VPSSBL_INTSTAT_CCDC_VDINT1 (1 << 1)
#define VPSSBL_INTSTAT_CCDC_VDINT0 (1 << 0)
/* DM365 ISP5 bit definitions */
#define ISP5_INTSTAT_VENCINT (1 << 21)
#define ISP5_INTSTAT_OSDINT (1 << 20)
/* VMOD TVTYP options for HDMD=0 */
#define SDTV_NTSC 0
#define SDTV_PAL 1
/* VMOD TVTYP options for HDMD=1 */
#define HDTV_525P 0
#define HDTV_625P 1
#define HDTV_1080I 2
#define HDTV_720P 3
#define OSD_MODE_CS (1 << 15)
#define OSD_MODE_OVRSZ (1 << 14)
#define OSD_MODE_OHRSZ (1 << 13)
#define OSD_MODE_EF (1 << 12)
#define OSD_MODE_VVRSZ (1 << 11)
#define OSD_MODE_VHRSZ (1 << 10)
#define OSD_MODE_FSINV (1 << 9)
#define OSD_MODE_BCLUT (1 << 8)
#define OSD_MODE_CABG_SHIFT 0
#define OSD_MODE_CABG (0xff << 0)
#define OSD_VIDWINMD_VFINV (1 << 15)
#define OSD_VIDWINMD_V1EFC (1 << 14)
#define OSD_VIDWINMD_VHZ1_SHIFT 12
#define OSD_VIDWINMD_VHZ1 (3 << 12)
#define OSD_VIDWINMD_VVZ1_SHIFT 10
#define OSD_VIDWINMD_VVZ1 (3 << 10)
#define OSD_VIDWINMD_VFF1 (1 << 9)
#define OSD_VIDWINMD_ACT1 (1 << 8)
#define OSD_VIDWINMD_V0EFC (1 << 6)
#define OSD_VIDWINMD_VHZ0_SHIFT 4
#define OSD_VIDWINMD_VHZ0 (3 << 4)
#define OSD_VIDWINMD_VVZ0_SHIFT 2
#define OSD_VIDWINMD_VVZ0 (3 << 2)
#define OSD_VIDWINMD_VFF0 (1 << 1)
#define OSD_VIDWINMD_ACT0 (1 << 0)
#define OSD_OSDWIN0MD_ATN0E (1 << 14)
#define OSD_OSDWIN0MD_RGB0E (1 << 13)
#define OSD_OSDWIN0MD_BMP0MD_SHIFT 13
#define OSD_OSDWIN0MD_BMP0MD (3 << 13)
#define OSD_OSDWIN0MD_CLUTS0 (1 << 12)
#define OSD_OSDWIN0MD_OHZ0_SHIFT 10
#define OSD_OSDWIN0MD_OHZ0 (3 << 10)
#define OSD_OSDWIN0MD_OVZ0_SHIFT 8
#define OSD_OSDWIN0MD_OVZ0 (3 << 8)
#define OSD_OSDWIN0MD_BMW0_SHIFT 6
#define OSD_OSDWIN0MD_BMW0 (3 << 6)
#define OSD_OSDWIN0MD_BLND0_SHIFT 3
#define OSD_OSDWIN0MD_BLND0 (7 << 3)
#define OSD_OSDWIN0MD_TE0 (1 << 2)
#define OSD_OSDWIN0MD_OFF0 (1 << 1)
#define OSD_OSDWIN0MD_OACT0 (1 << 0)
#define OSD_OSDWIN1MD_OASW (1 << 15)
#define OSD_OSDWIN1MD_ATN1E (1 << 14)
#define OSD_OSDWIN1MD_RGB1E (1 << 13)
#define OSD_OSDWIN1MD_BMP1MD_SHIFT 13
#define OSD_OSDWIN1MD_BMP1MD (3 << 13)
#define OSD_OSDWIN1MD_CLUTS1 (1 << 12)
#define OSD_OSDWIN1MD_OHZ1_SHIFT 10
#define OSD_OSDWIN1MD_OHZ1 (3 << 10)
#define OSD_OSDWIN1MD_OVZ1_SHIFT 8
#define OSD_OSDWIN1MD_OVZ1 (3 << 8)
#define OSD_OSDWIN1MD_BMW1_SHIFT 6
#define OSD_OSDWIN1MD_BMW1 (3 << 6)
#define OSD_OSDWIN1MD_BLND1_SHIFT 3
#define OSD_OSDWIN1MD_BLND1 (7 << 3)
#define OSD_OSDWIN1MD_TE1 (1 << 2)
#define OSD_OSDWIN1MD_OFF1 (1 << 1)
#define OSD_OSDWIN1MD_OACT1 (1 << 0)
#define OSD_OSDATRMD_OASW (1 << 15)
#define OSD_OSDATRMD_OHZA_SHIFT 10
#define OSD_OSDATRMD_OHZA (3 << 10)
#define OSD_OSDATRMD_OVZA_SHIFT 8
#define OSD_OSDATRMD_OVZA (3 << 8)
#define OSD_OSDATRMD_BLNKINT_SHIFT 6
#define OSD_OSDATRMD_BLNKINT (3 << 6)
#define OSD_OSDATRMD_OFFA (1 << 1)
#define OSD_OSDATRMD_BLNK (1 << 0)
#define OSD_RECTCUR_RCAD_SHIFT 8
#define OSD_RECTCUR_RCAD (0xff << 8)
#define OSD_RECTCUR_CLUTSR (1 << 7)
#define OSD_RECTCUR_RCHW_SHIFT 4
#define OSD_RECTCUR_RCHW (7 << 4)
#define OSD_RECTCUR_RCVW_SHIFT 1
#define OSD_RECTCUR_RCVW (7 << 1)
#define OSD_RECTCUR_RCACT (1 << 0)
#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0)
#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0)
#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0)
#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0)
#define OSD_WINOFST_AH_SHIFT 9
#define OSD_VIDWIN0OFST_V0AH (0xf << 9)
#define OSD_VIDWIN1OFST_V1AH (0xf << 9)
#define OSD_OSDWIN0OFST_O0AH (0xf << 9)
#define OSD_OSDWIN1OFST_O1AH (0xf << 9)
#define OSD_VIDWINADH_V1AH_SHIFT 8
#define OSD_VIDWINADH_V1AH (0x7f << 8)
#define OSD_VIDWINADH_V0AH_SHIFT 0
#define OSD_VIDWINADH_V0AH (0x7f << 0)
#define OSD_VIDWIN0ADL_V0AL (0xffff << 0)
#define OSD_VIDWIN1ADL_V1AL (0xffff << 0)
#define OSD_OSDWINADH_O1AH_SHIFT 8
#define OSD_OSDWINADH_O1AH (0x7f << 8)
#define OSD_OSDWINADH_O0AH_SHIFT 0
#define OSD_OSDWINADH_O0AH (0x7f << 0)
#define OSD_OSDWIN0ADL_O0AL (0xffff << 0)
#define OSD_OSDWIN1ADL_O1AL (0xffff << 0)
#define OSD_BASEPX_BPX (0x3ff << 0)
#define OSD_BASEPY_BPY (0x1ff << 0)
#define OSD_VIDWIN0XP_V0X (0x7ff << 0)
#define OSD_VIDWIN0YP_V0Y (0x7ff << 0)
#define OSD_VIDWIN0XL_V0W (0x7ff << 0)
#define OSD_VIDWIN0YL_V0H (0x7ff << 0)
#define OSD_VIDWIN1XP_V1X (0x7ff << 0)
#define OSD_VIDWIN1YP_V1Y (0x7ff << 0)
#define OSD_VIDWIN1XL_V1W (0x7ff << 0)
#define OSD_VIDWIN1YL_V1H (0x7ff << 0)
#define OSD_OSDWIN0XP_W0X (0x7ff << 0)
#define OSD_OSDWIN0YP_W0Y (0x7ff << 0)
#define OSD_OSDWIN0XL_W0W (0x7ff << 0)
#define OSD_OSDWIN0YL_W0H (0x7ff << 0)
#define OSD_OSDWIN1XP_W1X (0x7ff << 0)
#define OSD_OSDWIN1YP_W1Y (0x7ff << 0)
#define OSD_OSDWIN1XL_W1W (0x7ff << 0)
#define OSD_OSDWIN1YL_W1H (0x7ff << 0)
#define OSD_CURXP_RCSX (0x7ff << 0)
#define OSD_CURYP_RCSY (0x7ff << 0)
#define OSD_CURXL_RCSW (0x7ff << 0)
#define OSD_CURYL_RCSH (0x7ff << 0)
#define OSD_EXTMODE_EXPMDSEL (1 << 15)
#define OSD_EXTMODE_SCRNHEXP_SHIFT 13
#define OSD_EXTMODE_SCRNHEXP (3 << 13)
#define OSD_EXTMODE_SCRNVEXP (1 << 12)
#define OSD_EXTMODE_OSD1BLDCHR (1 << 11)
#define OSD_EXTMODE_OSD0BLDCHR (1 << 10)
#define OSD_EXTMODE_ATNOSD1EN (1 << 9)
#define OSD_EXTMODE_ATNOSD0EN (1 << 8)
#define OSD_EXTMODE_OSDHRSZ15 (1 << 7)
#define OSD_EXTMODE_VIDHRSZ15 (1 << 6)
#define OSD_EXTMODE_ZMFILV1HEN (1 << 5)
#define OSD_EXTMODE_ZMFILV1VEN (1 << 4)
#define OSD_EXTMODE_ZMFILV0HEN (1 << 3)
#define OSD_EXTMODE_ZMFILV0VEN (1 << 2)
#define OSD_EXTMODE_EXPFILHEN (1 << 1)
#define OSD_EXTMODE_EXPFILVEN (1 << 0)
#define OSD_MISCCTL_BLDSEL (1 << 15)
#define OSD_MISCCTL_S420D (1 << 14)
#define OSD_MISCCTL_BMAPT (1 << 13)
#define OSD_MISCCTL_DM365M (1 << 12)
#define OSD_MISCCTL_RGBEN (1 << 7)
#define OSD_MISCCTL_RGBWIN (1 << 6)
#define OSD_MISCCTL_DMANG (1 << 6)
#define OSD_MISCCTL_TMON (1 << 5)
#define OSD_MISCCTL_RSEL (1 << 4)
#define OSD_MISCCTL_CPBSY (1 << 3)
#define OSD_MISCCTL_PPSW (1 << 2)
#define OSD_MISCCTL_PPRV (1 << 1)
#define OSD_CLUTRAMYCB_Y_SHIFT 8
#define OSD_CLUTRAMYCB_Y (0xff << 8)
#define OSD_CLUTRAMYCB_CB_SHIFT 0
#define OSD_CLUTRAMYCB_CB (0xff << 0)
#define OSD_CLUTRAMCR_CR_SHIFT 8
#define OSD_CLUTRAMCR_CR (0xff << 8)
#define OSD_CLUTRAMCR_CADDR_SHIFT 0
#define OSD_CLUTRAMCR_CADDR (0xff << 0)
#define OSD_TRANSPVAL_RGBTRANS (0xffff << 0)
#define OSD_TRANSPVALL_RGBL (0xffff << 0)
#define OSD_TRANSPVALU_Y_SHIFT 8
#define OSD_TRANSPVALU_Y (0xff << 8)
#define OSD_TRANSPVALU_RGBU_SHIFT 0
#define OSD_TRANSPVALU_RGBU (0xff << 0)
#define OSD_TRANSPBMPIDX_BMP1_SHIFT 8
#define OSD_TRANSPBMPIDX_BMP1 (0xff << 8)
#define OSD_TRANSPBMPIDX_BMP0_SHIFT 0
#define OSD_TRANSPBMPIDX_BMP0 0xff
#endif /* _DAVINCI_VPBE_H_ */

View file

@ -0,0 +1,699 @@
/*
* Copyright (C) 2010 Texas Instruments Inc
*
* 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 version 2.
*
* 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/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <mach/hardware.h>
#include <mach/mux.h>
#include <linux/platform_data/i2c-davinci.h>
#include <linux/io.h>
#include <media/davinci/vpbe_types.h>
#include <media/davinci/vpbe_venc.h>
#include <media/davinci/vpss.h>
#include <media/v4l2-device.h>
#include "vpbe_venc_regs.h"
#define MODULE_NAME "davinci-vpbe-venc"
static struct platform_device_id vpbe_venc_devtype[] = {
{
.name = DM644X_VPBE_VENC_SUBDEV_NAME,
.driver_data = VPBE_VERSION_1,
}, {
.name = DM365_VPBE_VENC_SUBDEV_NAME,
.driver_data = VPBE_VERSION_2,
}, {
.name = DM355_VPBE_VENC_SUBDEV_NAME,
.driver_data = VPBE_VERSION_3,
},
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype);
static int debug = 2;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level 0-2");
struct venc_state {
struct v4l2_subdev sd;
struct venc_callback *callback;
struct venc_platform_data *pdata;
struct device *pdev;
u32 output;
v4l2_std_id std;
spinlock_t lock;
void __iomem *venc_base;
void __iomem *vdaccfg_reg;
enum vpbe_version venc_type;
};
static inline struct venc_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct venc_state, sd);
}
static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset)
{
struct venc_state *venc = to_state(sd);
return readl(venc->venc_base + offset);
}
static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val)
{
struct venc_state *venc = to_state(sd);
writel(val, (venc->venc_base + offset));
return val;
}
static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset,
u32 val, u32 mask)
{
u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask);
venc_write(sd, offset, new_val);
return new_val;
}
static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val)
{
struct venc_state *venc = to_state(sd);
writel(val, venc->vdaccfg_reg);
val = readl(venc->vdaccfg_reg);
return val;
}
#define VDAC_COMPONENT 0x543
#define VDAC_S_VIDEO 0x210
/* This function sets the dac of the VPBE for various outputs
*/
static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index)
{
switch (out_index) {
case 0:
v4l2_dbg(debug, 1, sd, "Setting output to Composite\n");
venc_write(sd, VENC_DACSEL, 0);
break;
case 1:
v4l2_dbg(debug, 1, sd, "Setting output to Component\n");
venc_write(sd, VENC_DACSEL, VDAC_COMPONENT);
break;
case 2:
v4l2_dbg(debug, 1, sd, "Setting output to S-video\n");
venc_write(sd, VENC_DACSEL, VDAC_S_VIDEO);
break;
default:
return -EINVAL;
}
return 0;
}
static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
{
struct venc_state *venc = to_state(sd);
v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n");
if (benable) {
venc_write(sd, VENC_VMOD, 0);
venc_write(sd, VENC_CVBS, 0);
venc_write(sd, VENC_LCDOUT, 0);
venc_write(sd, VENC_HSPLS, 0);
venc_write(sd, VENC_HSTART, 0);
venc_write(sd, VENC_HVALID, 0);
venc_write(sd, VENC_HINT, 0);
venc_write(sd, VENC_VSPLS, 0);
venc_write(sd, VENC_VSTART, 0);
venc_write(sd, VENC_VVALID, 0);
venc_write(sd, VENC_VINT, 0);
venc_write(sd, VENC_YCCCTL, 0);
venc_write(sd, VENC_DACSEL, 0);
} else {
venc_write(sd, VENC_VMOD, 0);
/* disable VCLK output pin enable */
venc_write(sd, VENC_VIDCTL, 0x141);
/* Disable output sync pins */
venc_write(sd, VENC_SYNCCTL, 0);
/* Disable DCLOCK */
venc_write(sd, VENC_DCLKCTL, 0);
venc_write(sd, VENC_DRGBX1, 0x0000057C);
/* Disable LCD output control (accepting default polarity) */
venc_write(sd, VENC_LCDOUT, 0);
if (venc->venc_type != VPBE_VERSION_3)
venc_write(sd, VENC_CMPNT, 0x100);
venc_write(sd, VENC_HSPLS, 0);
venc_write(sd, VENC_HINT, 0);
venc_write(sd, VENC_HSTART, 0);
venc_write(sd, VENC_HVALID, 0);
venc_write(sd, VENC_VSPLS, 0);
venc_write(sd, VENC_VINT, 0);
venc_write(sd, VENC_VSTART, 0);
venc_write(sd, VENC_VVALID, 0);
venc_write(sd, VENC_HSDLY, 0);
venc_write(sd, VENC_VSDLY, 0);
venc_write(sd, VENC_YCCCTL, 0);
venc_write(sd, VENC_VSTARTA, 0);
/* Set OSD clock and OSD Sync Adavance registers */
venc_write(sd, VENC_OSDCLK0, 1);
venc_write(sd, VENC_OSDCLK1, 2);
}
}
static void
venc_enable_vpss_clock(int venc_type,
enum vpbe_enc_timings_type type,
unsigned int pclock)
{
if (venc_type == VPBE_VERSION_1)
return;
if (venc_type == VPBE_VERSION_2 && (type == VPBE_ENC_STD || (type ==
VPBE_ENC_DV_TIMINGS && pclock <= 27000000))) {
vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
vpss_enable_clock(VPSS_VPBE_CLOCK, 1);
return;
}
if (venc_type == VPBE_VERSION_3 && type == VPBE_ENC_STD)
vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 0);
}
#define VDAC_CONFIG_SD_V3 0x0E21A6B6
#define VDAC_CONFIG_SD_V2 0x081141CF
/*
* setting NTSC mode
*/
static int venc_set_ntsc(struct v4l2_subdev *sd)
{
u32 val;
struct venc_state *venc = to_state(sd);
struct venc_platform_data *pdata = venc->pdata;
v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n");
/* Setup clock at VPSS & VENC for SD */
vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0)
return -EINVAL;
venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_525_60);
venc_enabledigitaloutput(sd, 0);
if (venc->venc_type == VPBE_VERSION_3) {
venc_write(sd, VENC_CLKCTL, 0x01);
venc_write(sd, VENC_VIDCTL, 0);
val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
} else if (venc->venc_type == VPBE_VERSION_2) {
venc_write(sd, VENC_CLKCTL, 0x01);
venc_write(sd, VENC_VIDCTL, 0);
vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
} else {
/* to set VENC CLK DIV to 1 - final clock is 54 MHz */
venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
/* Set REC656 Mode */
venc_write(sd, VENC_YCCCTL, 0x1);
venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ);
venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS);
}
venc_write(sd, VENC_VMOD, 0);
venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
VENC_VMOD_VIE);
venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT),
VENC_VMOD_TVTYP);
venc_write(sd, VENC_DACTST, 0x0);
venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
return 0;
}
/*
* setting PAL mode
*/
static int venc_set_pal(struct v4l2_subdev *sd)
{
struct venc_state *venc = to_state(sd);
v4l2_dbg(debug, 2, sd, "venc_set_pal\n");
/* Setup clock at VPSS & VENC for SD */
vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0)
return -EINVAL;
venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_625_50);
venc_enabledigitaloutput(sd, 0);
if (venc->venc_type == VPBE_VERSION_3) {
venc_write(sd, VENC_CLKCTL, 0x1);
venc_write(sd, VENC_VIDCTL, 0);
vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
} else if (venc->venc_type == VPBE_VERSION_2) {
venc_write(sd, VENC_CLKCTL, 0x1);
venc_write(sd, VENC_VIDCTL, 0);
vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
} else {
/* to set VENC CLK DIV to 1 - final clock is 54 MHz */
venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
/* Set REC656 Mode */
venc_write(sd, VENC_YCCCTL, 0x1);
}
venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT,
VENC_SYNCCTL_OVD);
venc_write(sd, VENC_VMOD, 0);
venc_modify(sd, VENC_VMOD,
(1 << VENC_VMOD_VIE_SHIFT),
VENC_VMOD_VIE);
venc_modify(sd, VENC_VMOD,
(0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
venc_modify(sd, VENC_VMOD,
(1 << VENC_VMOD_TVTYP_SHIFT),
VENC_VMOD_TVTYP);
venc_write(sd, VENC_DACTST, 0x0);
venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
return 0;
}
#define VDAC_CONFIG_HD_V2 0x081141EF
/*
* venc_set_480p59_94
*
* This function configures the video encoder to EDTV(525p) component setting.
*/
static int venc_set_480p59_94(struct v4l2_subdev *sd)
{
struct venc_state *venc = to_state(sd);
struct venc_platform_data *pdata = venc->pdata;
v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n");
if (venc->venc_type != VPBE_VERSION_1 &&
venc->venc_type != VPBE_VERSION_2)
return -EINVAL;
/* Setup clock at VPSS & VENC for SD */
if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0)
return -EINVAL;
venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000);
venc_enabledigitaloutput(sd, 0);
if (venc->venc_type == VPBE_VERSION_2)
vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
venc_write(sd, VENC_OSDCLK0, 0);
venc_write(sd, VENC_OSDCLK1, 1);
if (venc->venc_type == VPBE_VERSION_1) {
venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
VENC_VDPRO_DAFRQ);
venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
VENC_VDPRO_DAUPS);
}
venc_write(sd, VENC_VMOD, 0);
venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
VENC_VMOD_VIE);
venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT),
VENC_VMOD_TVTYP);
venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
return 0;
}
/*
* venc_set_625p
*
* This function configures the video encoder to HDTV(625p) component setting
*/
static int venc_set_576p50(struct v4l2_subdev *sd)
{
struct venc_state *venc = to_state(sd);
struct venc_platform_data *pdata = venc->pdata;
v4l2_dbg(debug, 2, sd, "venc_set_576p50\n");
if (venc->venc_type != VPBE_VERSION_1 &&
venc->venc_type != VPBE_VERSION_2)
return -EINVAL;
/* Setup clock at VPSS & VENC for SD */
if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0)
return -EINVAL;
venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000);
venc_enabledigitaloutput(sd, 0);
if (venc->venc_type == VPBE_VERSION_2)
vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
venc_write(sd, VENC_OSDCLK0, 0);
venc_write(sd, VENC_OSDCLK1, 1);
if (venc->venc_type == VPBE_VERSION_1) {
venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
VENC_VDPRO_DAFRQ);
venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
VENC_VDPRO_DAUPS);
}
venc_write(sd, VENC_VMOD, 0);
venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
VENC_VMOD_VIE);
venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT),
VENC_VMOD_TVTYP);
venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
return 0;
}
/*
* venc_set_720p60_internal - Setup 720p60 in venc for dm365 only
*/
static int venc_set_720p60_internal(struct v4l2_subdev *sd)
{
struct venc_state *venc = to_state(sd);
struct venc_platform_data *pdata = venc->pdata;
if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0)
return -EINVAL;
venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000);
venc_enabledigitaloutput(sd, 0);
venc_write(sd, VENC_OSDCLK0, 0);
venc_write(sd, VENC_OSDCLK1, 1);
venc_write(sd, VENC_VMOD, 0);
/* DM365 component HD mode */
venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
VENC_VMOD_VIE);
venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
venc_modify(sd, VENC_VMOD, (HDTV_720P << VENC_VMOD_TVTYP_SHIFT),
VENC_VMOD_TVTYP);
venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
venc_write(sd, VENC_XHINTVL, 0);
return 0;
}
/*
* venc_set_1080i30_internal - Setup 1080i30 in venc for dm365 only
*/
static int venc_set_1080i30_internal(struct v4l2_subdev *sd)
{
struct venc_state *venc = to_state(sd);
struct venc_platform_data *pdata = venc->pdata;
if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0)
return -EINVAL;
venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000);
venc_enabledigitaloutput(sd, 0);
venc_write(sd, VENC_OSDCLK0, 0);
venc_write(sd, VENC_OSDCLK1, 1);
venc_write(sd, VENC_VMOD, 0);
/* DM365 component HD mode */
venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
VENC_VMOD_VIE);
venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
venc_modify(sd, VENC_VMOD, (HDTV_1080I << VENC_VMOD_TVTYP_SHIFT),
VENC_VMOD_TVTYP);
venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
venc_write(sd, VENC_XHINTVL, 0);
return 0;
}
static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
{
v4l2_dbg(debug, 1, sd, "venc_s_std_output\n");
if (norm & V4L2_STD_525_60)
return venc_set_ntsc(sd);
else if (norm & V4L2_STD_625_50)
return venc_set_pal(sd);
return -EINVAL;
}
static int venc_s_dv_timings(struct v4l2_subdev *sd,
struct v4l2_dv_timings *dv_timings)
{
struct venc_state *venc = to_state(sd);
u32 height = dv_timings->bt.height;
int ret;
v4l2_dbg(debug, 1, sd, "venc_s_dv_timings\n");
if (height == 576)
return venc_set_576p50(sd);
else if (height == 480)
return venc_set_480p59_94(sd);
else if ((height == 720) &&
(venc->venc_type == VPBE_VERSION_2)) {
/* TBD setup internal 720p mode here */
ret = venc_set_720p60_internal(sd);
/* for DM365 VPBE, there is DAC inside */
vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
return ret;
} else if ((height == 1080) &&
(venc->venc_type == VPBE_VERSION_2)) {
/* TBD setup internal 1080i mode here */
ret = venc_set_1080i30_internal(sd);
/* for DM365 VPBE, there is DAC inside */
vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
return ret;
}
return -EINVAL;
}
static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
u32 config)
{
struct venc_state *venc = to_state(sd);
int ret;
v4l2_dbg(debug, 1, sd, "venc_s_routing\n");
ret = venc_set_dac(sd, output);
if (!ret)
venc->output = output;
return ret;
}
static long venc_ioctl(struct v4l2_subdev *sd,
unsigned int cmd,
void *arg)
{
u32 val;
switch (cmd) {
case VENC_GET_FLD:
val = venc_read(sd, VENC_VSTAT);
*((int *)arg) = ((val & VENC_VSTAT_FIDST) ==
VENC_VSTAT_FIDST);
break;
default:
v4l2_err(sd, "Wrong IOCTL cmd\n");
break;
}
return 0;
}
static const struct v4l2_subdev_core_ops venc_core_ops = {
.ioctl = venc_ioctl,
};
static const struct v4l2_subdev_video_ops venc_video_ops = {
.s_routing = venc_s_routing,
.s_std_output = venc_s_std_output,
.s_dv_timings = venc_s_dv_timings,
};
static const struct v4l2_subdev_ops venc_ops = {
.core = &venc_core_ops,
.video = &venc_video_ops,
};
static int venc_initialize(struct v4l2_subdev *sd)
{
struct venc_state *venc = to_state(sd);
int ret;
/* Set default to output to composite and std to NTSC */
venc->output = 0;
venc->std = V4L2_STD_525_60;
ret = venc_s_routing(sd, 0, venc->output, 0);
if (ret < 0) {
v4l2_err(sd, "Error setting output during init\n");
return -EINVAL;
}
ret = venc_s_std_output(sd, venc->std);
if (ret < 0) {
v4l2_err(sd, "Error setting std during init\n");
return -EINVAL;
}
return ret;
}
static int venc_device_get(struct device *dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct venc_state **venc = data;
if (strstr(pdev->name, "vpbe-venc") != NULL)
*venc = platform_get_drvdata(pdev);
return 0;
}
struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev,
const char *venc_name)
{
struct venc_state *venc;
int err;
err = bus_for_each_dev(&platform_bus_type, NULL, &venc,
venc_device_get);
if (venc == NULL)
return NULL;
v4l2_subdev_init(&venc->sd, &venc_ops);
strcpy(venc->sd.name, venc_name);
if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) {
v4l2_err(v4l2_dev,
"vpbe unable to register venc sub device\n");
return NULL;
}
if (venc_initialize(&venc->sd)) {
v4l2_err(v4l2_dev,
"vpbe venc initialization failed\n");
return NULL;
}
return &venc->sd;
}
EXPORT_SYMBOL(venc_sub_dev_init);
static int venc_probe(struct platform_device *pdev)
{
const struct platform_device_id *pdev_id;
struct venc_state *venc;
struct resource *res;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "No platform data for VENC sub device");
return -EINVAL;
}
pdev_id = platform_get_device_id(pdev);
if (!pdev_id)
return -EINVAL;
venc = devm_kzalloc(&pdev->dev, sizeof(struct venc_state), GFP_KERNEL);
if (venc == NULL)
return -ENOMEM;
venc->venc_type = pdev_id->driver_data;
venc->pdev = &pdev->dev;
venc->pdata = pdev->dev.platform_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
venc->venc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(venc->venc_base))
return PTR_ERR(venc->venc_base);
if (venc->venc_type != VPBE_VERSION_1) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
venc->vdaccfg_reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(venc->vdaccfg_reg))
return PTR_ERR(venc->vdaccfg_reg);
}
spin_lock_init(&venc->lock);
platform_set_drvdata(pdev, venc);
dev_notice(venc->pdev, "VENC sub device probe success\n");
return 0;
}
static int venc_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver venc_driver = {
.probe = venc_probe,
.remove = venc_remove,
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
},
.id_table = vpbe_venc_devtype
};
module_platform_driver(venc_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VPBE VENC Driver");
MODULE_AUTHOR("Texas Instruments");

View file

@ -0,0 +1,177 @@
/*
* Copyright (C) 2006-2010 Texas Instruments Inc
*
* 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 version 2..
*
* 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 _VPBE_VENC_REGS_H
#define _VPBE_VENC_REGS_H
/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */
#define VENC_VMOD 0x00
#define VENC_VIDCTL 0x04
#define VENC_VDPRO 0x08
#define VENC_SYNCCTL 0x0C
#define VENC_HSPLS 0x10
#define VENC_VSPLS 0x14
#define VENC_HINT 0x18
#define VENC_HSTART 0x1C
#define VENC_HVALID 0x20
#define VENC_VINT 0x24
#define VENC_VSTART 0x28
#define VENC_VVALID 0x2C
#define VENC_HSDLY 0x30
#define VENC_VSDLY 0x34
#define VENC_YCCCTL 0x38
#define VENC_RGBCTL 0x3C
#define VENC_RGBCLP 0x40
#define VENC_LINECTL 0x44
#define VENC_CULLLINE 0x48
#define VENC_LCDOUT 0x4C
#define VENC_BRTS 0x50
#define VENC_BRTW 0x54
#define VENC_ACCTL 0x58
#define VENC_PWMP 0x5C
#define VENC_PWMW 0x60
#define VENC_DCLKCTL 0x64
#define VENC_DCLKPTN0 0x68
#define VENC_DCLKPTN1 0x6C
#define VENC_DCLKPTN2 0x70
#define VENC_DCLKPTN3 0x74
#define VENC_DCLKPTN0A 0x78
#define VENC_DCLKPTN1A 0x7C
#define VENC_DCLKPTN2A 0x80
#define VENC_DCLKPTN3A 0x84
#define VENC_DCLKHS 0x88
#define VENC_DCLKHSA 0x8C
#define VENC_DCLKHR 0x90
#define VENC_DCLKVS 0x94
#define VENC_DCLKVR 0x98
#define VENC_CAPCTL 0x9C
#define VENC_CAPDO 0xA0
#define VENC_CAPDE 0xA4
#define VENC_ATR0 0xA8
#define VENC_ATR1 0xAC
#define VENC_ATR2 0xB0
#define VENC_VSTAT 0xB8
#define VENC_RAMADR 0xBC
#define VENC_RAMPORT 0xC0
#define VENC_DACTST 0xC4
#define VENC_YCOLVL 0xC8
#define VENC_SCPROG 0xCC
#define VENC_CVBS 0xDC
#define VENC_CMPNT 0xE0
#define VENC_ETMG0 0xE4
#define VENC_ETMG1 0xE8
#define VENC_ETMG2 0xEC
#define VENC_ETMG3 0xF0
#define VENC_DACSEL 0xF4
#define VENC_ARGBX0 0x100
#define VENC_ARGBX1 0x104
#define VENC_ARGBX2 0x108
#define VENC_ARGBX3 0x10C
#define VENC_ARGBX4 0x110
#define VENC_DRGBX0 0x114
#define VENC_DRGBX1 0x118
#define VENC_DRGBX2 0x11C
#define VENC_DRGBX3 0x120
#define VENC_DRGBX4 0x124
#define VENC_VSTARTA 0x128
#define VENC_OSDCLK0 0x12C
#define VENC_OSDCLK1 0x130
#define VENC_HVLDCL0 0x134
#define VENC_HVLDCL1 0x138
#define VENC_OSDHADV 0x13C
#define VENC_CLKCTL 0x140
#define VENC_GAMCTL 0x144
#define VENC_XHINTVL 0x174
/* bit definitions */
#define VPBE_PCR_VENC_DIV (1 << 1)
#define VPBE_PCR_CLK_OFF (1 << 0)
#define VENC_VMOD_VDMD_SHIFT 12
#define VENC_VMOD_VDMD_YCBCR16 0
#define VENC_VMOD_VDMD_YCBCR8 1
#define VENC_VMOD_VDMD_RGB666 2
#define VENC_VMOD_VDMD_RGB8 3
#define VENC_VMOD_VDMD_EPSON 4
#define VENC_VMOD_VDMD_CASIO 5
#define VENC_VMOD_VDMD_UDISPQVGA 6
#define VENC_VMOD_VDMD_STNLCD 7
#define VENC_VMOD_VIE_SHIFT 1
#define VENC_VMOD_VDMD (7 << 12)
#define VENC_VMOD_ITLCL (1 << 11)
#define VENC_VMOD_ITLC (1 << 10)
#define VENC_VMOD_NSIT (1 << 9)
#define VENC_VMOD_HDMD (1 << 8)
#define VENC_VMOD_TVTYP_SHIFT 6
#define VENC_VMOD_TVTYP (3 << 6)
#define VENC_VMOD_SLAVE (1 << 5)
#define VENC_VMOD_VMD (1 << 4)
#define VENC_VMOD_BLNK (1 << 3)
#define VENC_VMOD_VIE (1 << 1)
#define VENC_VMOD_VENC (1 << 0)
/* VMOD TVTYP options for HDMD=0 */
#define SDTV_NTSC 0
#define SDTV_PAL 1
/* VMOD TVTYP options for HDMD=1 */
#define HDTV_525P 0
#define HDTV_625P 1
#define HDTV_1080I 2
#define HDTV_720P 3
#define VENC_VIDCTL_VCLKP (1 << 14)
#define VENC_VIDCTL_VCLKE_SHIFT 13
#define VENC_VIDCTL_VCLKE (1 << 13)
#define VENC_VIDCTL_VCLKZ_SHIFT 12
#define VENC_VIDCTL_VCLKZ (1 << 12)
#define VENC_VIDCTL_SYDIR_SHIFT 8
#define VENC_VIDCTL_SYDIR (1 << 8)
#define VENC_VIDCTL_DOMD_SHIFT 4
#define VENC_VIDCTL_DOMD (3 << 4)
#define VENC_VIDCTL_YCDIR_SHIFT 0
#define VENC_VIDCTL_YCDIR (1 << 0)
#define VENC_VDPRO_ATYCC_SHIFT 5
#define VENC_VDPRO_ATYCC (1 << 5)
#define VENC_VDPRO_ATCOM_SHIFT 4
#define VENC_VDPRO_ATCOM (1 << 4)
#define VENC_VDPRO_DAFRQ (1 << 3)
#define VENC_VDPRO_DAUPS (1 << 2)
#define VENC_VDPRO_CUPS (1 << 1)
#define VENC_VDPRO_YUPS (1 << 0)
#define VENC_SYNCCTL_VPL_SHIFT 3
#define VENC_SYNCCTL_VPL (1 << 3)
#define VENC_SYNCCTL_HPL_SHIFT 2
#define VENC_SYNCCTL_HPL (1 << 2)
#define VENC_SYNCCTL_SYEV_SHIFT 1
#define VENC_SYNCCTL_SYEV (1 << 1)
#define VENC_SYNCCTL_SYEH_SHIFT 0
#define VENC_SYNCCTL_SYEH (1 << 0)
#define VENC_SYNCCTL_OVD_SHIFT 14
#define VENC_SYNCCTL_OVD (1 << 14)
#define VENC_DCLKCTL_DCKEC_SHIFT 11
#define VENC_DCLKCTL_DCKEC (1 << 11)
#define VENC_DCLKCTL_DCKPW_SHIFT 0
#define VENC_DCLKCTL_DCKPW (0x3f << 0)
#define VENC_VSTAT_FIDST (1 << 4)
#define VENC_CMPNT_MRGB_SHIFT 14
#define VENC_CMPNT_MRGB (1 << 14)
#endif /* _VPBE_VENC_REGS_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,488 @@
/*
* vpif - Video Port Interface driver
* VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
* that receiveing video byte stream and two channels(2, 3) for video output.
* The hardware supports SDTV, HDTV formats, raw data capture.
* Currently, the driver supports NTSC and PAL standards.
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
#include <linux/v4l2-dv-timings.h>
#include "vpif.h"
MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver");
MODULE_LICENSE("GPL");
#define VPIF_CH0_MAX_MODES 22
#define VPIF_CH1_MAX_MODES 2
#define VPIF_CH2_MAX_MODES 15
#define VPIF_CH3_MAX_MODES 2
spinlock_t vpif_lock;
EXPORT_SYMBOL_GPL(vpif_lock);
void __iomem *vpif_base;
EXPORT_SYMBOL_GPL(vpif_base);
/**
* vpif_ch_params: video standard configuration parameters for vpif
* The table must include all presets from supported subdevices.
*/
const struct vpif_channel_config_params vpif_ch_params[] = {
/* HDTV formats */
{
.name = "480p59_94",
.width = 720,
.height = 480,
.frm_fmt = 1,
.ycmux_mode = 0,
.eav2sav = 138-8,
.sav2eav = 720,
.l1 = 1,
.l3 = 43,
.l5 = 523,
.vsize = 525,
.capture_format = 0,
.vbi_supported = 0,
.hd_sd = 1,
.dv_timings = V4L2_DV_BT_CEA_720X480P59_94,
},
{
.name = "576p50",
.width = 720,
.height = 576,
.frm_fmt = 1,
.ycmux_mode = 0,
.eav2sav = 144-8,
.sav2eav = 720,
.l1 = 1,
.l3 = 45,
.l5 = 621,
.vsize = 625,
.capture_format = 0,
.vbi_supported = 0,
.hd_sd = 1,
.dv_timings = V4L2_DV_BT_CEA_720X576P50,
},
{
.name = "720p50",
.width = 1280,
.height = 720,
.frm_fmt = 1,
.ycmux_mode = 0,
.eav2sav = 700-8,
.sav2eav = 1280,
.l1 = 1,
.l3 = 26,
.l5 = 746,
.vsize = 750,
.capture_format = 0,
.vbi_supported = 0,
.hd_sd = 1,
.dv_timings = V4L2_DV_BT_CEA_1280X720P50,
},
{
.name = "720p60",
.width = 1280,
.height = 720,
.frm_fmt = 1,
.ycmux_mode = 0,
.eav2sav = 370 - 8,
.sav2eav = 1280,
.l1 = 1,
.l3 = 26,
.l5 = 746,
.vsize = 750,
.capture_format = 0,
.vbi_supported = 0,
.hd_sd = 1,
.dv_timings = V4L2_DV_BT_CEA_1280X720P60,
},
{
.name = "1080I50",
.width = 1920,
.height = 1080,
.frm_fmt = 0,
.ycmux_mode = 0,
.eav2sav = 720 - 8,
.sav2eav = 1920,
.l1 = 1,
.l3 = 21,
.l5 = 561,
.l7 = 563,
.l9 = 584,
.l11 = 1124,
.vsize = 1125,
.capture_format = 0,
.vbi_supported = 0,
.hd_sd = 1,
.dv_timings = V4L2_DV_BT_CEA_1920X1080I50,
},
{
.name = "1080I60",
.width = 1920,
.height = 1080,
.frm_fmt = 0,
.ycmux_mode = 0,
.eav2sav = 280 - 8,
.sav2eav = 1920,
.l1 = 1,
.l3 = 21,
.l5 = 561,
.l7 = 563,
.l9 = 584,
.l11 = 1124,
.vsize = 1125,
.capture_format = 0,
.vbi_supported = 0,
.hd_sd = 1,
.dv_timings = V4L2_DV_BT_CEA_1920X1080I60,
},
{
.name = "1080p60",
.width = 1920,
.height = 1080,
.frm_fmt = 1,
.ycmux_mode = 0,
.eav2sav = 280 - 8,
.sav2eav = 1920,
.l1 = 1,
.l3 = 42,
.l5 = 1122,
.vsize = 1125,
.capture_format = 0,
.vbi_supported = 0,
.hd_sd = 1,
.dv_timings = V4L2_DV_BT_CEA_1920X1080P60,
},
/* SDTV formats */
{
.name = "NTSC_M",
.width = 720,
.height = 480,
.frm_fmt = 0,
.ycmux_mode = 1,
.eav2sav = 268,
.sav2eav = 1440,
.l1 = 1,
.l3 = 23,
.l5 = 263,
.l7 = 266,
.l9 = 286,
.l11 = 525,
.vsize = 525,
.capture_format = 0,
.vbi_supported = 1,
.hd_sd = 0,
.stdid = V4L2_STD_525_60,
},
{
.name = "PAL_BDGHIK",
.width = 720,
.height = 576,
.frm_fmt = 0,
.ycmux_mode = 1,
.eav2sav = 280,
.sav2eav = 1440,
.l1 = 1,
.l3 = 23,
.l5 = 311,
.l7 = 313,
.l9 = 336,
.l11 = 624,
.vsize = 625,
.capture_format = 0,
.vbi_supported = 1,
.hd_sd = 0,
.stdid = V4L2_STD_625_50,
},
};
EXPORT_SYMBOL_GPL(vpif_ch_params);
const unsigned int vpif_ch_params_count = ARRAY_SIZE(vpif_ch_params);
EXPORT_SYMBOL_GPL(vpif_ch_params_count);
static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val)
{
if (val)
vpif_set_bit(reg, bit);
else
vpif_clr_bit(reg, bit);
}
/* This structure is used to keep track of VPIF size register's offsets */
struct vpif_registers {
u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl;
u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt;
u32 vanc1_size, width_mask, len_mask;
u8 max_modes;
};
static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = {
/* Channel0 */
{
VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01,
VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL,
VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
VPIF_CH0_MAX_MODES,
},
/* Channel1 */
{
VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01,
VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL,
VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
VPIF_CH1_MAX_MODES,
},
/* Channel2 */
{
VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01,
VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL,
VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE,
VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF,
VPIF_CH2_MAX_MODES
},
/* Channel3 */
{
VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01,
VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL,
VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE,
VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF,
VPIF_CH3_MAX_MODES
},
};
/* vpif_set_mode_info:
* This function is used to set horizontal and vertical config parameters
* As per the standard in the channel, configure the values of L1, L3,
* L5, L7 L9, L11 in VPIF Register , also write width and height
*/
static void vpif_set_mode_info(const struct vpif_channel_config_params *config,
u8 channel_id, u8 config_channel_id)
{
u32 value;
value = (config->eav2sav & vpifregs[config_channel_id].width_mask);
value <<= VPIF_CH_LEN_SHIFT;
value |= (config->sav2eav & vpifregs[config_channel_id].width_mask);
regw(value, vpifregs[channel_id].h_cfg);
value = (config->l1 & vpifregs[config_channel_id].len_mask);
value <<= VPIF_CH_LEN_SHIFT;
value |= (config->l3 & vpifregs[config_channel_id].len_mask);
regw(value, vpifregs[channel_id].v_cfg_00);
value = (config->l5 & vpifregs[config_channel_id].len_mask);
value <<= VPIF_CH_LEN_SHIFT;
value |= (config->l7 & vpifregs[config_channel_id].len_mask);
regw(value, vpifregs[channel_id].v_cfg_01);
value = (config->l9 & vpifregs[config_channel_id].len_mask);
value <<= VPIF_CH_LEN_SHIFT;
value |= (config->l11 & vpifregs[config_channel_id].len_mask);
regw(value, vpifregs[channel_id].v_cfg_02);
value = (config->vsize & vpifregs[config_channel_id].len_mask);
regw(value, vpifregs[channel_id].v_cfg);
}
/* config_vpif_params
* Function to set the parameters of a channel
* Mainly modifies the channel ciontrol register
* It sets frame format, yc mux mode
*/
static void config_vpif_params(struct vpif_params *vpifparams,
u8 channel_id, u8 found)
{
const struct vpif_channel_config_params *config = &vpifparams->std_info;
u32 value, ch_nip, reg;
u8 start, end;
int i;
start = channel_id;
end = channel_id + found;
for (i = start; i < end; i++) {
reg = vpifregs[i].ch_ctrl;
if (channel_id < 2)
ch_nip = VPIF_CAPTURE_CH_NIP;
else
ch_nip = VPIF_DISPLAY_CH_NIP;
vpif_wr_bit(reg, ch_nip, config->frm_fmt);
vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode);
vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT,
vpifparams->video_params.storage_mode);
/* Set raster scanning SDR Format */
vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT);
vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format);
if (channel_id > 1) /* Set the Pixel enable bit */
vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT);
else if (config->capture_format) {
/* Set the polarity of various pins */
vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT,
vpifparams->iface.fid_pol);
vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT,
vpifparams->iface.vd_pol);
vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT,
vpifparams->iface.hd_pol);
value = regr(reg);
/* Set data width */
value &= ~(0x3u <<
VPIF_CH_DATA_WIDTH_BIT);
value |= ((vpifparams->params.data_sz) <<
VPIF_CH_DATA_WIDTH_BIT);
regw(value, reg);
}
/* Write the pitch in the driver */
regw((vpifparams->video_params.hpitch),
vpifregs[i].line_offset);
}
}
/* vpif_set_video_params
* This function is used to set video parameters in VPIF register
*/
int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id)
{
const struct vpif_channel_config_params *config = &vpifparams->std_info;
int found = 1;
vpif_set_mode_info(config, channel_id, channel_id);
if (!config->ycmux_mode) {
/* YC are on separate channels (HDTV formats) */
vpif_set_mode_info(config, channel_id + 1, channel_id);
found = 2;
}
config_vpif_params(vpifparams, channel_id, found);
regw(0x80, VPIF_REQ_SIZE);
regw(0x01, VPIF_EMULATION_CTRL);
return found;
}
EXPORT_SYMBOL(vpif_set_video_params);
void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
u8 channel_id)
{
u32 value;
value = 0x3F8 & (vbiparams->hstart0);
value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16);
regw(value, vpifregs[channel_id].vanc0_strt);
value = 0x3F8 & (vbiparams->hstart1);
value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16);
regw(value, vpifregs[channel_id].vanc1_strt);
value = 0x3F8 & (vbiparams->hsize0);
value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16);
regw(value, vpifregs[channel_id].vanc0_size);
value = 0x3F8 & (vbiparams->hsize1);
value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16);
regw(value, vpifregs[channel_id].vanc1_size);
}
EXPORT_SYMBOL(vpif_set_vbi_display_params);
int vpif_channel_getfid(u8 channel_id)
{
return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK)
>> VPIF_CH_FID_SHIFT;
}
EXPORT_SYMBOL(vpif_channel_getfid);
static int vpif_probe(struct platform_device *pdev)
{
static struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vpif_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(vpif_base))
return PTR_ERR(vpif_base);
pm_runtime_enable(&pdev->dev);
pm_runtime_get(&pdev->dev);
spin_lock_init(&vpif_lock);
dev_info(&pdev->dev, "vpif probe success\n");
return 0;
}
static int vpif_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM
static int vpif_suspend(struct device *dev)
{
pm_runtime_put(dev);
return 0;
}
static int vpif_resume(struct device *dev)
{
pm_runtime_get(dev);
return 0;
}
static const struct dev_pm_ops vpif_pm = {
.suspend = vpif_suspend,
.resume = vpif_resume,
};
#define vpif_pm_ops (&vpif_pm)
#else
#define vpif_pm_ops NULL
#endif
static struct platform_driver vpif_driver = {
.driver = {
.name = "vpif",
.owner = THIS_MODULE,
.pm = vpif_pm_ops,
},
.remove = vpif_remove,
.probe = vpif_probe,
};
static void vpif_exit(void)
{
platform_driver_unregister(&vpif_driver);
}
static int __init vpif_init(void)
{
return platform_driver_register(&vpif_driver);
}
subsys_initcall(vpif_init);
module_exit(vpif_exit);

View file

@ -0,0 +1,688 @@
/*
* VPIF header file
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef VPIF_H
#define VPIF_H
#include <linux/io.h>
#include <linux/videodev2.h>
#include <media/davinci/vpif_types.h>
/* Maximum channel allowed */
#define VPIF_NUM_CHANNELS (4)
#define VPIF_CAPTURE_NUM_CHANNELS (2)
#define VPIF_DISPLAY_NUM_CHANNELS (2)
/* Macros to read/write registers */
extern void __iomem *vpif_base;
extern spinlock_t vpif_lock;
#define regr(reg) readl((reg) + vpif_base)
#define regw(value, reg) writel(value, (reg + vpif_base))
/* Register Address Offsets */
#define VPIF_PID (0x0000)
#define VPIF_CH0_CTRL (0x0004)
#define VPIF_CH1_CTRL (0x0008)
#define VPIF_CH2_CTRL (0x000C)
#define VPIF_CH3_CTRL (0x0010)
#define VPIF_INTEN (0x0020)
#define VPIF_INTEN_SET (0x0024)
#define VPIF_INTEN_CLR (0x0028)
#define VPIF_STATUS (0x002C)
#define VPIF_STATUS_CLR (0x0030)
#define VPIF_EMULATION_CTRL (0x0034)
#define VPIF_REQ_SIZE (0x0038)
#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040)
#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044)
#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048)
#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c)
#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050)
#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054)
#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058)
#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c)
#define VPIF_CH0_SP_CFG (0x0060)
#define VPIF_CH0_IMG_ADD_OFST (0x0064)
#define VPIF_CH0_HANC_ADD_OFST (0x0068)
#define VPIF_CH0_H_CFG (0x006c)
#define VPIF_CH0_V_CFG_00 (0x0070)
#define VPIF_CH0_V_CFG_01 (0x0074)
#define VPIF_CH0_V_CFG_02 (0x0078)
#define VPIF_CH0_V_CFG_03 (0x007c)
#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080)
#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084)
#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088)
#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c)
#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090)
#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094)
#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098)
#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c)
#define VPIF_CH1_SP_CFG (0x00a0)
#define VPIF_CH1_IMG_ADD_OFST (0x00a4)
#define VPIF_CH1_HANC_ADD_OFST (0x00a8)
#define VPIF_CH1_H_CFG (0x00ac)
#define VPIF_CH1_V_CFG_00 (0x00b0)
#define VPIF_CH1_V_CFG_01 (0x00b4)
#define VPIF_CH1_V_CFG_02 (0x00b8)
#define VPIF_CH1_V_CFG_03 (0x00bc)
#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0)
#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4)
#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8)
#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc)
#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0)
#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4)
#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8)
#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc)
#define VPIF_CH2_SP_CFG (0x00e0)
#define VPIF_CH2_IMG_ADD_OFST (0x00e4)
#define VPIF_CH2_HANC_ADD_OFST (0x00e8)
#define VPIF_CH2_H_CFG (0x00ec)
#define VPIF_CH2_V_CFG_00 (0x00f0)
#define VPIF_CH2_V_CFG_01 (0x00f4)
#define VPIF_CH2_V_CFG_02 (0x00f8)
#define VPIF_CH2_V_CFG_03 (0x00fc)
#define VPIF_CH2_HANC0_STRT (0x0100)
#define VPIF_CH2_HANC0_SIZE (0x0104)
#define VPIF_CH2_HANC1_STRT (0x0108)
#define VPIF_CH2_HANC1_SIZE (0x010c)
#define VPIF_CH2_VANC0_STRT (0x0110)
#define VPIF_CH2_VANC0_SIZE (0x0114)
#define VPIF_CH2_VANC1_STRT (0x0118)
#define VPIF_CH2_VANC1_SIZE (0x011c)
#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140)
#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144)
#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148)
#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c)
#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150)
#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154)
#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158)
#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c)
#define VPIF_CH3_SP_CFG (0x0160)
#define VPIF_CH3_IMG_ADD_OFST (0x0164)
#define VPIF_CH3_HANC_ADD_OFST (0x0168)
#define VPIF_CH3_H_CFG (0x016c)
#define VPIF_CH3_V_CFG_00 (0x0170)
#define VPIF_CH3_V_CFG_01 (0x0174)
#define VPIF_CH3_V_CFG_02 (0x0178)
#define VPIF_CH3_V_CFG_03 (0x017c)
#define VPIF_CH3_HANC0_STRT (0x0180)
#define VPIF_CH3_HANC0_SIZE (0x0184)
#define VPIF_CH3_HANC1_STRT (0x0188)
#define VPIF_CH3_HANC1_SIZE (0x018c)
#define VPIF_CH3_VANC0_STRT (0x0190)
#define VPIF_CH3_VANC0_SIZE (0x0194)
#define VPIF_CH3_VANC1_STRT (0x0198)
#define VPIF_CH3_VANC1_SIZE (0x019c)
#define VPIF_IODFT_CTRL (0x01c0)
/* Functions for bit Manipulation */
static inline void vpif_set_bit(u32 reg, u32 bit)
{
regw((regr(reg)) | (0x01 << bit), reg);
}
static inline void vpif_clr_bit(u32 reg, u32 bit)
{
regw(((regr(reg)) & ~(0x01 << bit)), reg);
}
/* Macro for Generating mask */
#ifdef GENERATE_MASK
#undef GENERATE_MASK
#endif
#define GENERATE_MASK(bits, pos) \
((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos)
/* Bit positions in the channel control registers */
#define VPIF_CH_DATA_MODE_BIT (2)
#define VPIF_CH_YC_MUX_BIT (3)
#define VPIF_CH_SDR_FMT_BIT (4)
#define VPIF_CH_HANC_EN_BIT (8)
#define VPIF_CH_VANC_EN_BIT (9)
#define VPIF_CAPTURE_CH_NIP (10)
#define VPIF_DISPLAY_CH_NIP (11)
#define VPIF_DISPLAY_PIX_EN_BIT (10)
#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12)
#define VPIF_CH_FID_POLARITY_BIT (15)
#define VPIF_CH_V_VALID_POLARITY_BIT (14)
#define VPIF_CH_H_VALID_POLARITY_BIT (13)
#define VPIF_CH_DATA_WIDTH_BIT (28)
#define VPIF_CH_CLK_EDGE_CTRL_BIT (31)
/* Mask various length */
#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0)
#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0)
#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0)
#define VPIF_CH_LEN_SHIFT (16)
/* VPIF masks for registers */
#define VPIF_REQ_SIZE_MASK (0x1ff)
/* bit posotion of interrupt vpif_ch_intr register */
#define VPIF_INTEN_FRAME_CH0 (0x00000001)
#define VPIF_INTEN_FRAME_CH1 (0x00000002)
#define VPIF_INTEN_FRAME_CH2 (0x00000004)
#define VPIF_INTEN_FRAME_CH3 (0x00000008)
/* bit position of clock and channel enable in vpif_chn_ctrl register */
#define VPIF_CH0_CLK_EN (0x00000002)
#define VPIF_CH0_EN (0x00000001)
#define VPIF_CH1_CLK_EN (0x00000002)
#define VPIF_CH1_EN (0x00000001)
#define VPIF_CH2_CLK_EN (0x00000002)
#define VPIF_CH2_EN (0x00000001)
#define VPIF_CH3_CLK_EN (0x00000002)
#define VPIF_CH3_EN (0x00000001)
#define VPIF_CH_CLK_EN (0x00000002)
#define VPIF_CH_EN (0x00000001)
#define VPIF_INT_TOP (0x00)
#define VPIF_INT_BOTTOM (0x01)
#define VPIF_INT_BOTH (0x02)
#define VPIF_CH0_INT_CTRL_SHIFT (6)
#define VPIF_CH1_INT_CTRL_SHIFT (6)
#define VPIF_CH2_INT_CTRL_SHIFT (6)
#define VPIF_CH3_INT_CTRL_SHIFT (6)
#define VPIF_CH_INT_CTRL_SHIFT (6)
#define VPIF_CH2_CLIP_ANC_EN 14
#define VPIF_CH2_CLIP_ACTIVE_EN 13
#define VPIF_CH3_CLIP_ANC_EN 14
#define VPIF_CH3_CLIP_ACTIVE_EN 13
/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL))
/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL))
/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL))
#define VPIF_CH_FID_MASK (0x20)
#define VPIF_CH_FID_SHIFT (5)
#define VPIF_NTSC_VBI_START_FIELD0 (1)
#define VPIF_NTSC_VBI_START_FIELD1 (263)
#define VPIF_PAL_VBI_START_FIELD0 (624)
#define VPIF_PAL_VBI_START_FIELD1 (311)
#define VPIF_NTSC_HBI_START_FIELD0 (1)
#define VPIF_NTSC_HBI_START_FIELD1 (263)
#define VPIF_PAL_HBI_START_FIELD0 (624)
#define VPIF_PAL_HBI_START_FIELD1 (311)
#define VPIF_NTSC_VBI_COUNT_FIELD0 (20)
#define VPIF_NTSC_VBI_COUNT_FIELD1 (19)
#define VPIF_PAL_VBI_COUNT_FIELD0 (24)
#define VPIF_PAL_VBI_COUNT_FIELD1 (25)
#define VPIF_NTSC_HBI_COUNT_FIELD0 (263)
#define VPIF_NTSC_HBI_COUNT_FIELD1 (262)
#define VPIF_PAL_HBI_COUNT_FIELD0 (312)
#define VPIF_PAL_HBI_COUNT_FIELD1 (313)
#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720)
#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720)
#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268)
#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280)
#define VPIF_CH_VANC_EN (0x20)
#define VPIF_DMA_REQ_SIZE (0x080)
#define VPIF_EMULATION_DISABLE (0x01)
extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS];
/* inline function to enable/disable channel0 */
static inline void enable_channel0(int enable)
{
if (enable)
regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL);
else
regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL);
}
/* inline function to enable/disable channel1 */
static inline void enable_channel1(int enable)
{
if (enable)
regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL);
else
regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL);
}
/* inline function to enable interrupt for channel0 */
static inline void channel0_intr_enable(int enable)
{
unsigned long flags;
spin_lock_irqsave(&vpif_lock, flags);
if (enable) {
regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
VPIF_INTEN_SET);
} else {
regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
VPIF_INTEN_SET);
}
spin_unlock_irqrestore(&vpif_lock, flags);
}
/* inline function to enable interrupt for channel1 */
static inline void channel1_intr_enable(int enable)
{
unsigned long flags;
spin_lock_irqsave(&vpif_lock, flags);
if (enable) {
regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
VPIF_INTEN_SET);
} else {
regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
VPIF_INTEN_SET);
}
spin_unlock_irqrestore(&vpif_lock, flags);
}
/* inline function to set buffer addresses in case of Y/C non mux mode */
static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
}
/* inline function to set buffer addresses in VPIF registers for video data */
static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA);
}
static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
}
static inline void ch0_set_vbi_addr(unsigned long top_vbi,
unsigned long btm_vbi, unsigned long a, unsigned long b)
{
regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC);
regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC);
}
static inline void ch0_set_hbi_addr(unsigned long top_vbi,
unsigned long btm_vbi, unsigned long a, unsigned long b)
{
regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC);
regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC);
}
static inline void ch1_set_vbi_addr(unsigned long top_vbi,
unsigned long btm_vbi, unsigned long a, unsigned long b)
{
regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC);
regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC);
}
static inline void ch1_set_hbi_addr(unsigned long top_vbi,
unsigned long btm_vbi, unsigned long a, unsigned long b)
{
regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC);
regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC);
}
/* Inline function to enable raw vbi in the given channel */
static inline void disable_raw_feature(u8 channel_id, u8 index)
{
u32 ctrl_reg;
if (0 == channel_id)
ctrl_reg = VPIF_CH0_CTRL;
else
ctrl_reg = VPIF_CH1_CTRL;
if (1 == index)
vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
else
vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
}
static inline void enable_raw_feature(u8 channel_id, u8 index)
{
u32 ctrl_reg;
if (0 == channel_id)
ctrl_reg = VPIF_CH0_CTRL;
else
ctrl_reg = VPIF_CH1_CTRL;
if (1 == index)
vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
else
vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
}
/* inline function to enable/disable channel2 */
static inline void enable_channel2(int enable)
{
if (enable) {
regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL);
} else {
regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL);
}
}
/* inline function to enable/disable channel3 */
static inline void enable_channel3(int enable)
{
if (enable) {
regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL);
} else {
regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL);
}
}
/* inline function to enable interrupt for channel2 */
static inline void channel2_intr_enable(int enable)
{
unsigned long flags;
spin_lock_irqsave(&vpif_lock, flags);
if (enable) {
regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
VPIF_INTEN_SET);
} else {
regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
VPIF_INTEN_SET);
}
spin_unlock_irqrestore(&vpif_lock, flags);
}
/* inline function to enable interrupt for channel3 */
static inline void channel3_intr_enable(int enable)
{
unsigned long flags;
spin_lock_irqsave(&vpif_lock, flags);
if (enable) {
regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
VPIF_INTEN_SET);
} else {
regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
VPIF_INTEN_SET);
}
spin_unlock_irqrestore(&vpif_lock, flags);
}
/* inline function to enable raw vbi data for channel2 */
static inline void channel2_raw_enable(int enable, u8 index)
{
u32 mask;
if (1 == index)
mask = VPIF_CH_VANC_EN_BIT;
else
mask = VPIF_CH_HANC_EN_BIT;
if (enable)
vpif_set_bit(VPIF_CH2_CTRL, mask);
else
vpif_clr_bit(VPIF_CH2_CTRL, mask);
}
/* inline function to enable raw vbi data for channel3*/
static inline void channel3_raw_enable(int enable, u8 index)
{
u32 mask;
if (1 == index)
mask = VPIF_CH_VANC_EN_BIT;
else
mask = VPIF_CH_HANC_EN_BIT;
if (enable)
vpif_set_bit(VPIF_CH3_CTRL, mask);
else
vpif_clr_bit(VPIF_CH3_CTRL, mask);
}
/* function to enable clipping (for both active and blanking regions) on ch 2 */
static inline void channel2_clipping_enable(int enable)
{
if (enable) {
vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
} else {
vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
}
}
/* function to enable clipping (for both active and blanking regions) on ch 3 */
static inline void channel3_clipping_enable(int enable)
{
if (enable) {
vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
} else {
vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
}
}
/* inline function to set buffer addresses in case of Y/C non mux mode */
static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
}
/* inline function to set buffer addresses in VPIF registers for video data */
static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA);
}
static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
}
/* inline function to set buffer addresses in VPIF registers for vbi data */
static inline void ch2_set_vbi_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC);
regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC);
}
static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC);
regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
}
static inline int vpif_intr_status(int channel)
{
int status = 0;
int mask;
if (channel < 0 || channel > 3)
return 0;
mask = 1 << channel;
status = regr(VPIF_STATUS) & mask;
regw(status, VPIF_STATUS_CLR);
return status;
}
#define VPIF_MAX_NAME (30)
/* This structure will store size parameters as per the mode selected by user */
struct vpif_channel_config_params {
char name[VPIF_MAX_NAME]; /* Name of the mode */
u16 width; /* Indicates width of the image */
u16 height; /* Indicates height of the image */
u8 frm_fmt; /* Interlaced (0) or progressive (1) */
u8 ycmux_mode; /* This mode requires one (0) or two (1)
channels */
u16 eav2sav; /* length of eav 2 sav */
u16 sav2eav; /* length of sav 2 eav */
u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */
u16 vsize; /* Vertical size of the image */
u8 capture_format; /* Indicates whether capture format
* is in BT or in CCD/CMOS */
u8 vbi_supported; /* Indicates whether this mode
* supports capturing vbi or not */
u8 hd_sd; /* HDTV (1) or SDTV (0) format */
v4l2_std_id stdid; /* SDTV format */
struct v4l2_dv_timings dv_timings; /* HDTV format */
};
extern const unsigned int vpif_ch_params_count;
extern const struct vpif_channel_config_params vpif_ch_params[];
struct vpif_video_params;
struct vpif_params;
struct vpif_vbi_params;
int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id);
void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
u8 channel_id);
int vpif_channel_getfid(u8 channel_id);
enum data_size {
_8BITS = 0,
_10BITS,
_12BITS,
};
/* Structure for vpif parameters for raw vbi data */
struct vpif_vbi_params {
__u32 hstart0; /* Horizontal start of raw vbi data for first field */
__u32 vstart0; /* Vertical start of raw vbi data for first field */
__u32 hsize0; /* Horizontal size of raw vbi data for first field */
__u32 vsize0; /* Vertical size of raw vbi data for first field */
__u32 hstart1; /* Horizontal start of raw vbi data for second field */
__u32 vstart1; /* Vertical start of raw vbi data for second field */
__u32 hsize1; /* Horizontal size of raw vbi data for second field */
__u32 vsize1; /* Vertical size of raw vbi data for second field */
};
/* structure for vpif parameters */
struct vpif_video_params {
__u8 storage_mode; /* Indicates field or frame mode */
unsigned long hpitch;
v4l2_std_id stdid;
};
struct vpif_params {
struct vpif_interface iface;
struct vpif_video_params video_params;
struct vpif_channel_config_params std_info;
union param {
struct vpif_vbi_params vbi_params;
enum data_size data_sz;
} params;
};
#endif /* End of #ifndef VPIF_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
/*
* Copyright (C) 2009 Texas Instruments Inc
*
* 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 VPIF_CAPTURE_H
#define VPIF_CAPTURE_H
/* Header files */
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-device.h>
#include "vpif.h"
/* Macros */
#define VPIF_CAPTURE_VERSION "0.0.2"
#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \
(V4L2_FIELD_NONE == field)) || \
(((V4L2_FIELD_INTERLACED == field) || \
(V4L2_FIELD_SEQ_TB == field)) || \
(V4L2_FIELD_SEQ_BT == field)))
#define VPIF_CAPTURE_MAX_DEVICES 2
#define VPIF_VIDEO_INDEX 0
#define VPIF_NUMBER_OF_OBJECTS 1
/* Enumerated data type to give id to each device per channel */
enum vpif_channel_id {
VPIF_CHANNEL0_VIDEO = 0,
VPIF_CHANNEL1_VIDEO,
};
struct video_obj {
enum v4l2_field buf_field;
/* Currently selected or default standard */
v4l2_std_id stdid;
struct v4l2_dv_timings dv_timings;
};
struct vpif_cap_buffer {
struct vb2_buffer vb;
struct list_head list;
};
struct common_obj {
/* Pointer pointing to current v4l2_buffer */
struct vpif_cap_buffer *cur_frm;
/* Pointer pointing to current v4l2_buffer */
struct vpif_cap_buffer *next_frm;
/* Used to store pixel format */
struct v4l2_format fmt;
/* Buffer queue used in video-buf */
struct vb2_queue buffer_queue;
/* allocator-specific contexts for each plane */
struct vb2_alloc_ctx *alloc_ctx;
/* Queue of filled frames */
struct list_head dma_queue;
/* Used in video-buf */
spinlock_t irqlock;
/* lock used to access this structure */
struct mutex lock;
/* Function pointer to set the addresses */
void (*set_addr) (unsigned long, unsigned long, unsigned long,
unsigned long);
/* offset where Y top starts from the starting of the buffer */
u32 ytop_off;
/* offset where Y bottom starts from the starting of the buffer */
u32 ybtm_off;
/* offset where C top starts from the starting of the buffer */
u32 ctop_off;
/* offset where C bottom starts from the starting of the buffer */
u32 cbtm_off;
/* Indicates width of the image data */
u32 width;
/* Indicates height of the image data */
u32 height;
};
struct channel_obj {
/* Identifies video device for this channel */
struct video_device *video_dev;
/* Indicates id of the field which is being displayed */
u32 field_id;
/* flag to indicate whether decoder is initialized */
u8 initialized;
/* Identifies channel */
enum vpif_channel_id channel_id;
/* Current input */
u32 input_idx;
/* subdev corresponding to the current input, may be NULL */
struct v4l2_subdev *sd;
/* vpif configuration params */
struct vpif_params vpifparams;
/* common object array */
struct common_obj common[VPIF_NUMBER_OF_OBJECTS];
/* video object */
struct video_obj video;
};
struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
struct v4l2_subdev **sd;
struct v4l2_async_notifier notifier;
struct vpif_capture_config *config;
};
#endif /* VPIF_CAPTURE_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,127 @@
/*
* VPIF display header file
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef VPIF_DISPLAY_H
#define VPIF_DISPLAY_H
/* Header files */
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-device.h>
#include "vpif.h"
/* Macros */
#define VPIF_DISPLAY_VERSION "0.0.2"
#define VPIF_VALID_FIELD(field) \
(((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \
(((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \
(V4L2_FIELD_SEQ_BT == field)))
#define VPIF_DISPLAY_MAX_DEVICES (2)
#define VPIF_SLICED_BUF_SIZE (256)
#define VPIF_SLICED_MAX_SERVICES (3)
#define VPIF_VIDEO_INDEX (0)
#define VPIF_VBI_INDEX (1)
#define VPIF_HBI_INDEX (2)
/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/
#define VPIF_NUMOBJECTS (1)
/* Macros */
#define ISALIGNED(a) (0 == ((a) & 7))
/* enumerated data types */
/* Enumerated data type to give id to each device per channel */
enum vpif_channel_id {
VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */
VPIF_CHANNEL3_VIDEO, /* Channel3 Video */
};
/* structures */
struct video_obj {
enum v4l2_field buf_field;
u32 latest_only; /* indicate whether to return
* most recent displayed frame only */
v4l2_std_id stdid; /* Currently selected or default
* standard */
struct v4l2_dv_timings dv_timings;
};
struct vpif_disp_buffer {
struct vb2_buffer vb;
struct list_head list;
};
struct common_obj {
struct vpif_disp_buffer *cur_frm; /* Pointer pointing to current
* vb2_buffer */
struct vpif_disp_buffer *next_frm; /* Pointer pointing to next
* vb2_buffer */
struct v4l2_format fmt; /* Used to store the format */
struct vb2_queue buffer_queue; /* Buffer queue used in
* video-buf */
/* allocator-specific contexts for each plane */
struct vb2_alloc_ctx *alloc_ctx;
struct list_head dma_queue; /* Queue of filled frames */
spinlock_t irqlock; /* Used in video-buf */
/* channel specific parameters */
struct mutex lock; /* lock used to access this
* structure */
u32 ytop_off; /* offset of Y top from the
* starting of the buffer */
u32 ybtm_off; /* offset of Y bottom from the
* starting of the buffer */
u32 ctop_off; /* offset of C top from the
* starting of the buffer */
u32 cbtm_off; /* offset of C bottom from the
* starting of the buffer */
/* Function pointer to set the addresses */
void (*set_addr)(unsigned long, unsigned long,
unsigned long, unsigned long);
u32 height;
u32 width;
};
struct channel_obj {
/* V4l2 specific parameters */
struct video_device *video_dev; /* Identifies video device for
* this channel */
u32 field_id; /* Indicates id of the field
* which is being displayed */
u8 initialized; /* flag to indicate whether
* encoder is initialized */
u32 output_idx; /* Current output index */
struct v4l2_subdev *sd; /* Current output subdev(may be NULL) */
enum vpif_channel_id channel_id;/* Identifies channel */
struct vpif_params vpifparams;
struct common_obj common[VPIF_NUMOBJECTS];
struct video_obj video;
};
/* vpif device structure */
struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
struct v4l2_subdev **sd;
struct v4l2_async_notifier notifier;
struct vpif_display_config *config;
};
#endif /* VPIF_DISPLAY_H */

View file

@ -0,0 +1,533 @@
/*
* Copyright (C) 2009 Texas Instruments.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* common vpss system module platform driver for all video drivers.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
#include <media/davinci/vpss.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VPSS Driver");
MODULE_AUTHOR("Texas Instruments");
/* DM644x defines */
#define DM644X_SBL_PCR_VPSS (4)
#define DM355_VPSSBL_INTSEL 0x10
#define DM355_VPSSBL_EVTSEL 0x14
/* vpss BL register offsets */
#define DM355_VPSSBL_CCDCMUX 0x1c
/* vpss CLK register offsets */
#define DM355_VPSSCLK_CLKCTRL 0x04
/* masks and shifts */
#define VPSS_HSSISEL_SHIFT 4
/*
* VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4,
* IPIPE_INT1_SDR - vpss_int5
*/
#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10
/* VENCINT - vpss_int8 */
#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4
#define DM365_ISP5_PCCR 0x04
#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0)
#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1)
#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2)
#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3)
#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4)
#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5)
#define DM365_ISP5_PCCR_RSV BIT(6)
#define DM365_ISP5_BCR 0x08
#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1)
#define DM365_ISP5_INTSEL1 0x10
#define DM365_ISP5_INTSEL2 0x14
#define DM365_ISP5_INTSEL3 0x18
#define DM365_ISP5_CCDCMUX 0x20
#define DM365_ISP5_PG_FRAME_SIZE 0x28
#define DM365_VPBE_CLK_CTRL 0x00
#define VPSS_CLK_CTRL 0x01c40044
#define VPSS_CLK_CTRL_VENCCLKEN BIT(3)
#define VPSS_CLK_CTRL_DACCLKEN BIT(4)
/*
* vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1,
* AF - vpss_int3
*/
#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100
/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */
#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f
/* VENC - vpss_int8 */
#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015
/* masks and shifts for DM365*/
#define DM365_CCDC_PG_VD_POL_SHIFT 0
#define DM365_CCDC_PG_HD_POL_SHIFT 1
#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4))
#define CCD_SRC_SEL_SHIFT 4
/* Different SoC platforms supported by this driver */
enum vpss_platform_type {
DM644X,
DM355,
DM365,
};
/*
* vpss operations. Depends on platform. Not all functions are available
* on all platforms. The api, first check if a function is available before
* invoking it. In the probe, the function ptrs are initialized based on
* vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc.
*/
struct vpss_hw_ops {
/* enable clock */
int (*enable_clock)(enum vpss_clock_sel clock_sel, int en);
/* select input to ccdc */
void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
/* clear wbl overflow bit */
int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
/* set sync polarity */
void (*set_sync_pol)(struct vpss_sync_pol);
/* set the PG_FRAME_SIZE register*/
void (*set_pg_frame_size)(struct vpss_pg_frame_size);
/* check and clear interrupt if occurred */
int (*dma_complete_interrupt)(void);
};
/* vpss configuration */
struct vpss_oper_config {
__iomem void *vpss_regs_base0;
__iomem void *vpss_regs_base1;
resource_size_t *vpss_regs_base2;
enum vpss_platform_type platform;
spinlock_t vpss_lock;
struct vpss_hw_ops hw_ops;
};
static struct vpss_oper_config oper_cfg;
/* register access routines */
static inline u32 bl_regr(u32 offset)
{
return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
}
static inline void bl_regw(u32 val, u32 offset)
{
__raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
}
static inline u32 vpss_regr(u32 offset)
{
return __raw_readl(oper_cfg.vpss_regs_base1 + offset);
}
static inline void vpss_regw(u32 val, u32 offset)
{
__raw_writel(val, oper_cfg.vpss_regs_base1 + offset);
}
/* For DM365 only */
static inline u32 isp5_read(u32 offset)
{
return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
}
/* For DM365 only */
static inline void isp5_write(u32 val, u32 offset)
{
__raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
}
static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
{
u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK;
/* if we are using pattern generator, enable it */
if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG)
temp |= 0x08;
temp |= (src_sel << CCD_SRC_SEL_SHIFT);
isp5_write(temp, DM365_ISP5_CCDCMUX);
}
static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
{
bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX);
}
int vpss_dma_complete_interrupt(void)
{
if (!oper_cfg.hw_ops.dma_complete_interrupt)
return 2;
return oper_cfg.hw_ops.dma_complete_interrupt();
}
EXPORT_SYMBOL(vpss_dma_complete_interrupt);
int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
{
if (!oper_cfg.hw_ops.select_ccdc_source)
return -EINVAL;
oper_cfg.hw_ops.select_ccdc_source(src_sel);
return 0;
}
EXPORT_SYMBOL(vpss_select_ccdc_source);
static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
{
u32 mask = 1, val;
if (wbl_sel < VPSS_PCR_AEW_WBL_0 ||
wbl_sel > VPSS_PCR_CCDC_WBL_O)
return -EINVAL;
/* writing a 0 clear the overflow */
mask = ~(mask << wbl_sel);
val = bl_regr(DM644X_SBL_PCR_VPSS) & mask;
bl_regw(val, DM644X_SBL_PCR_VPSS);
return 0;
}
void vpss_set_sync_pol(struct vpss_sync_pol sync)
{
if (!oper_cfg.hw_ops.set_sync_pol)
return;
oper_cfg.hw_ops.set_sync_pol(sync);
}
EXPORT_SYMBOL(vpss_set_sync_pol);
int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
{
if (!oper_cfg.hw_ops.clear_wbl_overflow)
return -EINVAL;
return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel);
}
EXPORT_SYMBOL(vpss_clear_wbl_overflow);
/*
* dm355_enable_clock - Enable VPSS Clock
* @clock_sel: Clock to be enabled/disabled
* @en: enable/disable flag
*
* This is called to enable or disable a vpss clock
*/
static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en)
{
unsigned long flags;
u32 utemp, mask = 0x1, shift = 0;
switch (clock_sel) {
case VPSS_VPBE_CLOCK:
/* nothing since lsb */
break;
case VPSS_VENC_CLOCK_SEL:
shift = 2;
break;
case VPSS_CFALD_CLOCK:
shift = 3;
break;
case VPSS_H3A_CLOCK:
shift = 4;
break;
case VPSS_IPIPE_CLOCK:
shift = 5;
break;
case VPSS_CCDC_CLOCK:
shift = 6;
break;
default:
printk(KERN_ERR "dm355_enable_clock:"
" Invalid selector: %d\n", clock_sel);
return -EINVAL;
}
spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL);
if (!en)
utemp &= ~(mask << shift);
else
utemp |= (mask << shift);
vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL);
spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
return 0;
}
static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en)
{
unsigned long flags;
u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR;
u32 (*read)(u32 offset) = isp5_read;
void(*write)(u32 val, u32 offset) = isp5_write;
switch (clock_sel) {
case VPSS_BL_CLOCK:
break;
case VPSS_CCDC_CLOCK:
shift = 1;
break;
case VPSS_H3A_CLOCK:
shift = 2;
break;
case VPSS_RSZ_CLOCK:
shift = 3;
break;
case VPSS_IPIPE_CLOCK:
shift = 4;
break;
case VPSS_IPIPEIF_CLOCK:
shift = 5;
break;
case VPSS_PCLK_INTERNAL:
shift = 6;
break;
case VPSS_PSYNC_CLOCK_SEL:
shift = 7;
break;
case VPSS_VPBE_CLOCK:
read = vpss_regr;
write = vpss_regw;
offset = DM365_VPBE_CLK_CTRL;
break;
case VPSS_VENC_CLOCK_SEL:
shift = 2;
read = vpss_regr;
write = vpss_regw;
offset = DM365_VPBE_CLK_CTRL;
break;
case VPSS_LDC_CLOCK:
shift = 3;
read = vpss_regr;
write = vpss_regw;
offset = DM365_VPBE_CLK_CTRL;
break;
case VPSS_FDIF_CLOCK:
shift = 4;
read = vpss_regr;
write = vpss_regw;
offset = DM365_VPBE_CLK_CTRL;
break;
case VPSS_OSD_CLOCK_SEL:
shift = 6;
read = vpss_regr;
write = vpss_regw;
offset = DM365_VPBE_CLK_CTRL;
break;
case VPSS_LDC_CLOCK_SEL:
shift = 7;
read = vpss_regr;
write = vpss_regw;
offset = DM365_VPBE_CLK_CTRL;
break;
default:
printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n",
clock_sel);
return -1;
}
spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
utemp = read(offset);
if (!en) {
mask = ~mask;
utemp &= (mask << shift);
} else
utemp |= (mask << shift);
write(utemp, offset);
spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
return 0;
}
int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en)
{
if (!oper_cfg.hw_ops.enable_clock)
return -EINVAL;
return oper_cfg.hw_ops.enable_clock(clock_sel, en);
}
EXPORT_SYMBOL(vpss_enable_clock);
void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync)
{
int val = 0;
val = isp5_read(DM365_ISP5_CCDCMUX);
val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT);
val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT);
isp5_write(val, DM365_ISP5_CCDCMUX);
}
EXPORT_SYMBOL(dm365_vpss_set_sync_pol);
void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
{
if (!oper_cfg.hw_ops.set_pg_frame_size)
return;
oper_cfg.hw_ops.set_pg_frame_size(frame_size);
}
EXPORT_SYMBOL(vpss_set_pg_frame_size);
void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
{
int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16;
current_reg |= (frame_size.pplen - 1);
isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE);
}
EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
static int vpss_probe(struct platform_device *pdev)
{
struct resource *res;
char *platform_name;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data\n");
return -ENOENT;
}
platform_name = pdev->dev.platform_data;
if (!strcmp(platform_name, "dm355_vpss"))
oper_cfg.platform = DM355;
else if (!strcmp(platform_name, "dm365_vpss"))
oper_cfg.platform = DM365;
else if (!strcmp(platform_name, "dm644x_vpss"))
oper_cfg.platform = DM644X;
else {
dev_err(&pdev->dev, "vpss driver not supported on"
" this platform\n");
return -ENODEV;
}
dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
oper_cfg.vpss_regs_base0 = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(oper_cfg.vpss_regs_base0))
return PTR_ERR(oper_cfg.vpss_regs_base0);
if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
oper_cfg.vpss_regs_base1 = devm_ioremap_resource(&pdev->dev,
res);
if (IS_ERR(oper_cfg.vpss_regs_base1))
return PTR_ERR(oper_cfg.vpss_regs_base1);
}
if (oper_cfg.platform == DM355) {
oper_cfg.hw_ops.enable_clock = dm355_enable_clock;
oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source;
/* Setup vpss interrupts */
bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL);
bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL);
} else if (oper_cfg.platform == DM365) {
oper_cfg.hw_ops.enable_clock = dm365_enable_clock;
oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source;
/* Setup vpss interrupts */
isp5_write((isp5_read(DM365_ISP5_PCCR) |
DM365_ISP5_PCCR_BL_CLK_ENABLE |
DM365_ISP5_PCCR_ISIF_CLK_ENABLE |
DM365_ISP5_PCCR_H3A_CLK_ENABLE |
DM365_ISP5_PCCR_RSZ_CLK_ENABLE |
DM365_ISP5_PCCR_IPIPE_CLK_ENABLE |
DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE |
DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR);
isp5_write((isp5_read(DM365_ISP5_BCR) |
DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR);
isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1);
isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2);
isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3);
} else
oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
pm_runtime_enable(&pdev->dev);
pm_runtime_get(&pdev->dev);
spin_lock_init(&oper_cfg.vpss_lock);
dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
return 0;
}
static int vpss_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static int vpss_suspend(struct device *dev)
{
pm_runtime_put(dev);
return 0;
}
static int vpss_resume(struct device *dev)
{
pm_runtime_get(dev);
return 0;
}
static const struct dev_pm_ops vpss_pm_ops = {
.suspend = vpss_suspend,
.resume = vpss_resume,
};
static struct platform_driver vpss_driver = {
.driver = {
.name = "vpss",
.owner = THIS_MODULE,
.pm = &vpss_pm_ops,
},
.remove = vpss_remove,
.probe = vpss_probe,
};
static void vpss_exit(void)
{
iounmap(oper_cfg.vpss_regs_base2);
release_mem_region(VPSS_CLK_CTRL, 4);
platform_driver_unregister(&vpss_driver);
}
static int __init vpss_init(void)
{
if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control"))
return -EBUSY;
oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4);
writel(VPSS_CLK_CTRL_VENCCLKEN |
VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2);
return platform_driver_register(&vpss_driver);
}
subsys_initcall(vpss_init);
module_exit(vpss_exit);

View file

@ -0,0 +1,3 @@
exynos-gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o
obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,534 @@
/*
* Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* header file for Samsung EXYNOS5 SoC series G-Scaler driver
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef GSC_CORE_H_
#define GSC_CORE_H_
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <media/videobuf2-core.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mediabus.h>
#include <media/videobuf2-dma-contig.h>
#include "gsc-regs.h"
#define CONFIG_VB2_GSC_DMA_CONTIG 1
#define GSC_MODULE_NAME "exynos-gsc"
#define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
#define GSC_MAX_DEVS 4
#define GSC_M2M_BUF_NUM 0
#define GSC_MAX_CTRL_NUM 10
#define GSC_SC_ALIGN_4 4
#define GSC_SC_ALIGN_2 2
#define DEFAULT_CSC_EQ 1
#define DEFAULT_CSC_RANGE 1
#define GSC_PARAMS (1 << 0)
#define GSC_SRC_FMT (1 << 1)
#define GSC_DST_FMT (1 << 2)
#define GSC_CTX_M2M (1 << 3)
#define GSC_CTX_STOP_REQ (1 << 6)
#define GSC_CTX_ABORT (1 << 7)
enum gsc_dev_flags {
/* for global */
ST_SUSPEND,
/* for m2m node */
ST_M2M_OPEN,
ST_M2M_RUN,
ST_M2M_PEND,
ST_M2M_SUSPENDED,
ST_M2M_SUSPENDING,
};
enum gsc_irq {
GSC_IRQ_DONE,
GSC_IRQ_OVERRUN
};
/**
* enum gsc_datapath - the path of data used for G-Scaler
* @GSC_CAMERA: from camera
* @GSC_DMA: from/to DMA
* @GSC_LOCAL: to local path
* @GSC_WRITEBACK: from FIMD
*/
enum gsc_datapath {
GSC_CAMERA = 0x1,
GSC_DMA,
GSC_MIXER,
GSC_FIMD,
GSC_WRITEBACK,
};
enum gsc_color_fmt {
GSC_RGB = 0x1,
GSC_YUV420 = 0x2,
GSC_YUV422 = 0x4,
GSC_YUV444 = 0x8,
};
enum gsc_yuv_fmt {
GSC_LSB_Y = 0x10,
GSC_LSB_C,
GSC_CBCR = 0x20,
GSC_CRCB,
};
#define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh)
#define is_rgb(x) (!!((x) & 0x1))
#define is_yuv420(x) (!!((x) & 0x2))
#define is_yuv422(x) (!!((x) & 0x4))
#define gsc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
#define gsc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
#define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN, &(dev)->state)
#define ctrl_to_ctx(__ctrl) \
container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler)
/**
* struct gsc_fmt - the driver's internal color format data
* @mbus_code: Media Bus pixel code, -1 if not applicable
* @name: format description
* @pixelformat: the fourcc code for this format, 0 if not applicable
* @yorder: Y/C order
* @corder: Chrominance order control
* @num_planes: number of physically non-contiguous data planes
* @nr_comp: number of physically contiguous data planes
* @depth: per plane driver's private 'number of bits per pixel'
* @flags: flags indicating which operation mode format applies to
*/
struct gsc_fmt {
enum v4l2_mbus_pixelcode mbus_code;
char *name;
u32 pixelformat;
u32 color;
u32 yorder;
u32 corder;
u16 num_planes;
u16 num_comp;
u8 depth[VIDEO_MAX_PLANES];
u32 flags;
};
/**
* struct gsc_input_buf - the driver's video buffer
* @vb: videobuf2 buffer
* @list : linked list structure for buffer queue
* @idx : index of G-Scaler input buffer
*/
struct gsc_input_buf {
struct vb2_buffer vb;
struct list_head list;
int idx;
};
/**
* struct gsc_addr - the G-Scaler physical address set
* @y: luminance plane address
* @cb: Cb plane address
* @cr: Cr plane address
*/
struct gsc_addr {
dma_addr_t y;
dma_addr_t cb;
dma_addr_t cr;
};
/* struct gsc_ctrls - the G-Scaler control set
* @rotate: rotation degree
* @hflip: horizontal flip
* @vflip: vertical flip
* @global_alpha: the alpha value of current frame
*/
struct gsc_ctrls {
struct v4l2_ctrl *rotate;
struct v4l2_ctrl *hflip;
struct v4l2_ctrl *vflip;
struct v4l2_ctrl *global_alpha;
};
/**
* struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler
* @pre_shfactor: pre sclaer shift factor
* @pre_hratio: horizontal ratio of the prescaler
* @pre_vratio: vertical ratio of the prescaler
* @main_hratio: the main scaler's horizontal ratio
* @main_vratio: the main scaler's vertical ratio
*/
struct gsc_scaler {
u32 pre_shfactor;
u32 pre_hratio;
u32 pre_vratio;
u32 main_hratio;
u32 main_vratio;
};
struct gsc_dev;
struct gsc_ctx;
/**
* struct gsc_frame - source/target frame properties
* @f_width: SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH
* @f_height: SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT
* @crop: cropped(source)/scaled(destination) size
* @payload: image size in bytes (w x h x bpp)
* @addr: image frame buffer physical addresses
* @fmt: G-Scaler color format pointer
* @colorspace: value indicating v4l2_colorspace
* @alpha: frame's alpha value
*/
struct gsc_frame {
u32 f_width;
u32 f_height;
struct v4l2_rect crop;
unsigned long payload[VIDEO_MAX_PLANES];
struct gsc_addr addr;
const struct gsc_fmt *fmt;
u32 colorspace;
u8 alpha;
};
/**
* struct gsc_m2m_device - v4l2 memory-to-memory device data
* @vfd: the video device node for v4l2 m2m mode
* @m2m_dev: v4l2 memory-to-memory device data
* @ctx: hardware context data
* @refcnt: the reference counter
*/
struct gsc_m2m_device {
struct video_device *vfd;
struct v4l2_m2m_dev *m2m_dev;
struct gsc_ctx *ctx;
int refcnt;
};
/**
* struct gsc_pix_max - image pixel size limits in various IP configurations
*
* @org_scaler_bypass_w: max pixel width when the scaler is disabled
* @org_scaler_bypass_h: max pixel height when the scaler is disabled
* @org_scaler_input_w: max pixel width when the scaler is enabled
* @org_scaler_input_h: max pixel height when the scaler is enabled
* @real_rot_dis_w: max pixel src cropped height with the rotator is off
* @real_rot_dis_h: max pixel src croppped width with the rotator is off
* @real_rot_en_w: max pixel src cropped width with the rotator is on
* @real_rot_en_h: max pixel src cropped height with the rotator is on
* @target_rot_dis_w: max pixel dst scaled width with the rotator is off
* @target_rot_dis_h: max pixel dst scaled height with the rotator is off
* @target_rot_en_w: max pixel dst scaled width with the rotator is on
* @target_rot_en_h: max pixel dst scaled height with the rotator is on
*/
struct gsc_pix_max {
u16 org_scaler_bypass_w;
u16 org_scaler_bypass_h;
u16 org_scaler_input_w;
u16 org_scaler_input_h;
u16 real_rot_dis_w;
u16 real_rot_dis_h;
u16 real_rot_en_w;
u16 real_rot_en_h;
u16 target_rot_dis_w;
u16 target_rot_dis_h;
u16 target_rot_en_w;
u16 target_rot_en_h;
};
/**
* struct gsc_pix_min - image pixel size limits in various IP configurations
*
* @org_w: minimum source pixel width
* @org_h: minimum source pixel height
* @real_w: minimum input crop pixel width
* @real_h: minimum input crop pixel height
* @target_rot_dis_w: minimum output scaled pixel height when rotator is off
* @target_rot_dis_h: minimum output scaled pixel height when rotator is off
* @target_rot_en_w: minimum output scaled pixel height when rotator is on
* @target_rot_en_h: minimum output scaled pixel height when rotator is on
*/
struct gsc_pix_min {
u16 org_w;
u16 org_h;
u16 real_w;
u16 real_h;
u16 target_rot_dis_w;
u16 target_rot_dis_h;
u16 target_rot_en_w;
u16 target_rot_en_h;
};
struct gsc_pix_align {
u16 org_h;
u16 org_w;
u16 offset_h;
u16 real_w;
u16 real_h;
u16 target_w;
u16 target_h;
};
/**
* struct gsc_variant - G-Scaler variant information
*/
struct gsc_variant {
struct gsc_pix_max *pix_max;
struct gsc_pix_min *pix_min;
struct gsc_pix_align *pix_align;
u16 in_buf_cnt;
u16 out_buf_cnt;
u16 sc_up_max;
u16 sc_down_max;
u16 poly_sc_down_max;
u16 pre_sc_down_max;
u16 local_sc_down;
};
/**
* struct gsc_driverdata - per device type driver data for init time.
*
* @variant: the variant information for this driver.
* @lclk_frequency: G-Scaler clock frequency
* @num_entities: the number of g-scalers
*/
struct gsc_driverdata {
struct gsc_variant *variant[GSC_MAX_DEVS];
unsigned long lclk_frequency;
int num_entities;
};
/**
* struct gsc_dev - abstraction for G-Scaler entity
* @slock: the spinlock protecting this data structure
* @lock: the mutex protecting this data structure
* @pdev: pointer to the G-Scaler platform device
* @variant: the IP variant information
* @id: G-Scaler device index (0..GSC_MAX_DEVS)
* @clock: clocks required for G-Scaler operation
* @regs: the mapped hardware registers
* @irq_queue: interrupt handler waitqueue
* @m2m: memory-to-memory V4L2 device information
* @state: flags used to synchronize m2m and capture mode operation
* @alloc_ctx: videobuf2 memory allocator context
* @vdev: video device for G-Scaler instance
*/
struct gsc_dev {
spinlock_t slock;
struct mutex lock;
struct platform_device *pdev;
struct gsc_variant *variant;
u16 id;
struct clk *clock;
void __iomem *regs;
wait_queue_head_t irq_queue;
struct gsc_m2m_device m2m;
struct exynos_platform_gscaler *pdata;
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device vdev;
struct v4l2_device v4l2_dev;
};
/**
* gsc_ctx - the device context data
* @s_frame: source frame properties
* @d_frame: destination frame properties
* @in_path: input mode (DMA or camera)
* @out_path: output mode (DMA or FIFO)
* @scaler: image scaler properties
* @flags: additional flags for image conversion
* @state: flags to keep track of user configuration
* @gsc_dev: the G-Scaler device this context applies to
* @m2m_ctx: memory-to-memory device context
* @fh: v4l2 file handle
* @ctrl_handler: v4l2 controls handler
* @gsc_ctrls G-Scaler control set
* @ctrls_rdy: true if the control handler is initialized
*/
struct gsc_ctx {
struct gsc_frame s_frame;
struct gsc_frame d_frame;
enum gsc_datapath in_path;
enum gsc_datapath out_path;
struct gsc_scaler scaler;
u32 flags;
u32 state;
int rotation;
unsigned int hflip:1;
unsigned int vflip:1;
struct gsc_dev *gsc_dev;
struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_fh fh;
struct v4l2_ctrl_handler ctrl_handler;
struct gsc_ctrls gsc_ctrls;
bool ctrls_rdy;
};
void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm);
int gsc_register_m2m_device(struct gsc_dev *gsc);
void gsc_unregister_m2m_device(struct gsc_dev *gsc);
void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state);
u32 get_plane_size(struct gsc_frame *fr, unsigned int plane);
const struct gsc_fmt *get_format(int index);
const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index);
int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f);
int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
void gsc_set_frame_size(struct gsc_frame *frame, int width, int height);
int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h);
int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
u32 *ratio);
void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh);
void gsc_check_src_scale_info(struct gsc_variant *var,
struct gsc_frame *s_frame,
u32 *wratio, u32 tx, u32 ty, u32 *hratio);
int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
int dh, int rot, int out_path);
int gsc_set_scaler_info(struct gsc_ctx *ctx);
int gsc_ctrls_create(struct gsc_ctx *ctx);
void gsc_ctrls_delete(struct gsc_ctx *ctx);
int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
struct gsc_frame *frame, struct gsc_addr *addr);
static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx)
{
unsigned long flags;
spin_lock_irqsave(&ctx->gsc_dev->slock, flags);
ctx->state |= state;
spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
}
static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx)
{
unsigned long flags;
spin_lock_irqsave(&ctx->gsc_dev->slock, flags);
ctx->state &= ~state;
spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
}
static inline int is_tiled(const struct gsc_fmt *fmt)
{
return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16;
}
static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on)
{
u32 cfg = readl(dev->regs + GSC_ENABLE);
if (on)
cfg |= GSC_ENABLE_ON;
else
cfg &= ~GSC_ENABLE_ON;
writel(cfg, dev->regs + GSC_ENABLE);
}
static inline int gsc_hw_get_irq_status(struct gsc_dev *dev)
{
u32 cfg = readl(dev->regs + GSC_IRQ);
if (cfg & GSC_IRQ_STATUS_OR_IRQ)
return GSC_IRQ_OVERRUN;
else
return GSC_IRQ_DONE;
}
static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq)
{
u32 cfg = readl(dev->regs + GSC_IRQ);
if (irq == GSC_IRQ_OVERRUN)
cfg |= GSC_IRQ_STATUS_OR_IRQ;
else if (irq == GSC_IRQ_DONE)
cfg |= GSC_IRQ_STATUS_FRM_DONE_IRQ;
writel(cfg, dev->regs + GSC_IRQ);
}
static inline void gsc_lock(struct vb2_queue *vq)
{
struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
mutex_lock(&ctx->gsc_dev->lock);
}
static inline void gsc_unlock(struct vb2_queue *vq)
{
struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
mutex_unlock(&ctx->gsc_dev->lock);
}
static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx)
{
unsigned long flags;
bool ret;
spin_lock_irqsave(&ctx->gsc_dev->slock, flags);
ret = (ctx->state & mask) == mask;
spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
return ret;
}
static inline struct gsc_frame *ctx_get_frame(struct gsc_ctx *ctx,
enum v4l2_buf_type type)
{
struct gsc_frame *frame;
if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
frame = &ctx->s_frame;
} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
frame = &ctx->d_frame;
} else {
pr_err("Wrong buffer/video queue type (%d)", type);
return ERR_PTR(-EINVAL);
}
return frame;
}
void gsc_hw_set_sw_reset(struct gsc_dev *dev);
int gsc_wait_reset(struct gsc_dev *dev);
void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask);
void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask);
void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
int index);
void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr,
int index);
void gsc_hw_set_input_path(struct gsc_ctx *ctx);
void gsc_hw_set_in_size(struct gsc_ctx *ctx);
void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx);
void gsc_hw_set_in_image_format(struct gsc_ctx *ctx);
void gsc_hw_set_output_path(struct gsc_ctx *ctx);
void gsc_hw_set_out_size(struct gsc_ctx *ctx);
void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx);
void gsc_hw_set_out_image_format(struct gsc_ctx *ctx);
void gsc_hw_set_prescaler(struct gsc_ctx *ctx);
void gsc_hw_set_mainscaler(struct gsc_ctx *ctx);
void gsc_hw_set_rotation(struct gsc_ctx *ctx);
void gsc_hw_set_global_alpha(struct gsc_ctx *ctx);
void gsc_hw_set_sfr_update(struct gsc_ctx *ctx);
#endif /* GSC_CORE_H_ */

View file

@ -0,0 +1,792 @@
/*
* Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS5 SoC series G-Scaler driver
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/bug.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <media/v4l2-ioctl.h>
#include "gsc-core.h"
static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
{
struct gsc_ctx *curr_ctx;
struct gsc_dev *gsc = ctx->gsc_dev;
int ret;
curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx))
return 0;
gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx);
ret = wait_event_timeout(gsc->irq_queue,
!gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
GSC_SHUTDOWN_TIMEOUT);
return ret == 0 ? -ETIMEDOUT : ret;
}
static void __gsc_m2m_job_abort(struct gsc_ctx *ctx)
{
int ret;
ret = gsc_m2m_ctx_stop_req(ctx);
if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {
gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);
gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
}
}
static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct gsc_ctx *ctx = q->drv_priv;
int ret;
ret = pm_runtime_get_sync(&ctx->gsc_dev->pdev->dev);
return ret > 0 ? 0 : ret;
}
static void gsc_m2m_stop_streaming(struct vb2_queue *q)
{
struct gsc_ctx *ctx = q->drv_priv;
__gsc_m2m_job_abort(ctx);
pm_runtime_put(&ctx->gsc_dev->pdev->dev);
}
void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
{
struct vb2_buffer *src_vb, *dst_vb;
if (!ctx || !ctx->m2m_ctx)
return;
src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
if (src_vb && dst_vb) {
dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;
dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_vb->v4l2_buf.flags |=
src_vb->v4l2_buf.flags
& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
v4l2_m2m_buf_done(src_vb, vb_state);
v4l2_m2m_buf_done(dst_vb, vb_state);
v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev,
ctx->m2m_ctx);
}
}
static void gsc_m2m_job_abort(void *priv)
{
__gsc_m2m_job_abort((struct gsc_ctx *)priv);
}
static int gsc_get_bufs(struct gsc_ctx *ctx)
{
struct gsc_frame *s_frame, *d_frame;
struct vb2_buffer *src_vb, *dst_vb;
int ret;
s_frame = &ctx->s_frame;
d_frame = &ctx->d_frame;
src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
ret = gsc_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr);
if (ret)
return ret;
dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
ret = gsc_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr);
if (ret)
return ret;
dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
return 0;
}
static void gsc_m2m_device_run(void *priv)
{
struct gsc_ctx *ctx = priv;
struct gsc_dev *gsc;
unsigned long flags;
int ret;
bool is_set = false;
if (WARN(!ctx, "null hardware context\n"))
return;
gsc = ctx->gsc_dev;
spin_lock_irqsave(&gsc->slock, flags);
set_bit(ST_M2M_PEND, &gsc->state);
/* Reconfigure hardware if the context has changed. */
if (gsc->m2m.ctx != ctx) {
pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
gsc->m2m.ctx, ctx);
ctx->state |= GSC_PARAMS;
gsc->m2m.ctx = ctx;
}
is_set = ctx->state & GSC_CTX_STOP_REQ;
if (is_set) {
ctx->state &= ~GSC_CTX_STOP_REQ;
ctx->state |= GSC_CTX_ABORT;
wake_up(&gsc->irq_queue);
goto put_device;
}
ret = gsc_get_bufs(ctx);
if (ret) {
pr_err("Wrong address");
goto put_device;
}
gsc_set_prefbuf(gsc, &ctx->s_frame);
gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
if (ctx->state & GSC_PARAMS) {
gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
gsc_hw_set_frm_done_irq_mask(gsc, false);
gsc_hw_set_gsc_irq_enable(gsc, true);
if (gsc_set_scaler_info(ctx)) {
pr_err("Scaler setup error");
goto put_device;
}
gsc_hw_set_input_path(ctx);
gsc_hw_set_in_size(ctx);
gsc_hw_set_in_image_format(ctx);
gsc_hw_set_output_path(ctx);
gsc_hw_set_out_size(ctx);
gsc_hw_set_out_image_format(ctx);
gsc_hw_set_prescaler(ctx);
gsc_hw_set_mainscaler(ctx);
gsc_hw_set_rotation(ctx);
gsc_hw_set_global_alpha(ctx);
}
/* update shadow registers */
gsc_hw_set_sfr_update(ctx);
ctx->state &= ~GSC_PARAMS;
gsc_hw_enable_control(gsc, true);
spin_unlock_irqrestore(&gsc->slock, flags);
return;
put_device:
ctx->state &= ~GSC_PARAMS;
spin_unlock_irqrestore(&gsc->slock, flags);
}
static int gsc_m2m_queue_setup(struct vb2_queue *vq,
const struct v4l2_format *fmt,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *allocators[])
{
struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
struct gsc_frame *frame;
int i;
frame = ctx_get_frame(ctx, vq->type);
if (IS_ERR(frame))
return PTR_ERR(frame);
if (!frame->fmt)
return -EINVAL;
*num_planes = frame->fmt->num_planes;
for (i = 0; i < frame->fmt->num_planes; i++) {
sizes[i] = frame->payload[i];
allocators[i] = ctx->gsc_dev->alloc_ctx;
}
return 0;
}
static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
{
struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct gsc_frame *frame;
int i;
frame = ctx_get_frame(ctx, vb->vb2_queue->type);
if (IS_ERR(frame))
return PTR_ERR(frame);
if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
for (i = 0; i < frame->fmt->num_planes; i++)
vb2_set_plane_payload(vb, i, frame->payload[i]);
}
return 0;
}
static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
{
struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
if (ctx->m2m_ctx)
v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
}
static struct vb2_ops gsc_m2m_qops = {
.queue_setup = gsc_m2m_queue_setup,
.buf_prepare = gsc_m2m_buf_prepare,
.buf_queue = gsc_m2m_buf_queue,
.wait_prepare = gsc_unlock,
.wait_finish = gsc_lock,
.stop_streaming = gsc_m2m_stop_streaming,
.start_streaming = gsc_m2m_start_streaming,
};
static int gsc_m2m_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
struct gsc_dev *gsc = ctx->gsc_dev;
strlcpy(cap->driver, gsc->pdev->name, sizeof(cap->driver));
strlcpy(cap->card, gsc->pdev->name, sizeof(cap->card));
strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE |
V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int gsc_m2m_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
return gsc_enum_fmt_mplane(f);
}
static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
return gsc_g_fmt_mplane(ctx, f);
}
static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
return gsc_try_fmt_mplane(ctx, f);
}
static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
struct vb2_queue *vq;
struct gsc_frame *frame;
struct v4l2_pix_format_mplane *pix;
int i, ret = 0;
ret = gsc_m2m_try_fmt_mplane(file, fh, f);
if (ret)
return ret;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (vb2_is_streaming(vq)) {
pr_err("queue (%d) busy", f->type);
return -EBUSY;
}
if (V4L2_TYPE_IS_OUTPUT(f->type))
frame = &ctx->s_frame;
else
frame = &ctx->d_frame;
pix = &f->fmt.pix_mp;
frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
frame->colorspace = pix->colorspace;
if (!frame->fmt)
return -EINVAL;
for (i = 0; i < frame->fmt->num_planes; i++)
frame->payload[i] = pix->plane_fmt[i].sizeimage;
gsc_set_frame_size(frame, pix->width, pix->height);
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
else
gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
return 0;
}
static int gsc_m2m_reqbufs(struct file *file, void *fh,
struct v4l2_requestbuffers *reqbufs)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
struct gsc_dev *gsc = ctx->gsc_dev;
u32 max_cnt;
max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
if (reqbufs->count > max_cnt) {
return -EINVAL;
} else if (reqbufs->count == 0) {
if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx);
else
gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
}
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
static int gsc_m2m_expbuf(struct file *file, void *fh,
struct v4l2_exportbuffer *eb)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
}
static int gsc_m2m_querybuf(struct file *file, void *fh,
struct v4l2_buffer *buf)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
}
static int gsc_m2m_qbuf(struct file *file, void *fh,
struct v4l2_buffer *buf)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
}
static int gsc_m2m_dqbuf(struct file *file, void *fh,
struct v4l2_buffer *buf)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
}
static int gsc_m2m_streamon(struct file *file, void *fh,
enum v4l2_buf_type type)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
/* The source and target color format need to be set */
if (V4L2_TYPE_IS_OUTPUT(type)) {
if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
return -EINVAL;
} else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
return -EINVAL;
}
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
static int gsc_m2m_streamoff(struct file *file, void *fh,
enum v4l2_buf_type type)
{
struct gsc_ctx *ctx = fh_to_ctx(fh);
return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
}
/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
{
if (a->left < b->left || a->top < b->top)
return 0;
if (a->left + a->width > b->left + b->width)
return 0;
if (a->top + a->height > b->top + b->height)
return 0;
return 1;
}
static int gsc_m2m_g_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct gsc_frame *frame;
struct gsc_ctx *ctx = fh_to_ctx(fh);
if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
(s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
return -EINVAL;
frame = ctx_get_frame(ctx, s->type);
if (IS_ERR(frame))
return PTR_ERR(frame);
switch (s->target) {
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
s->r.left = 0;
s->r.top = 0;
s->r.width = frame->f_width;
s->r.height = frame->f_height;
return 0;
case V4L2_SEL_TGT_COMPOSE:
case V4L2_SEL_TGT_CROP:
s->r.left = frame->crop.left;
s->r.top = frame->crop.top;
s->r.width = frame->crop.width;
s->r.height = frame->crop.height;
return 0;
}
return -EINVAL;
}
static int gsc_m2m_s_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct gsc_frame *frame;
struct gsc_ctx *ctx = fh_to_ctx(fh);
struct v4l2_crop cr;
struct gsc_variant *variant = ctx->gsc_dev->variant;
int ret;
cr.type = s->type;
cr.c = s->r;
if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
(s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
return -EINVAL;
ret = gsc_try_crop(ctx, &cr);
if (ret)
return ret;
if (s->flags & V4L2_SEL_FLAG_LE &&
!is_rectangle_enclosed(&cr.c, &s->r))
return -ERANGE;
if (s->flags & V4L2_SEL_FLAG_GE &&
!is_rectangle_enclosed(&s->r, &cr.c))
return -ERANGE;
s->r = cr.c;
switch (s->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
case V4L2_SEL_TGT_COMPOSE:
frame = &ctx->s_frame;
break;
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP:
case V4L2_SEL_TGT_CROP_DEFAULT:
frame = &ctx->d_frame;
break;
default:
return -EINVAL;
}
/* Check to see if scaling ratio is within supported range */
if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
ret = gsc_check_scaler_ratio(variant, cr.c.width,
cr.c.height, ctx->d_frame.crop.width,
ctx->d_frame.crop.height,
ctx->gsc_ctrls.rotate->val, ctx->out_path);
} else {
ret = gsc_check_scaler_ratio(variant,
ctx->s_frame.crop.width,
ctx->s_frame.crop.height, cr.c.width,
cr.c.height, ctx->gsc_ctrls.rotate->val,
ctx->out_path);
}
if (ret) {
pr_err("Out of scaler range");
return -EINVAL;
}
}
frame->crop = cr.c;
gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
return 0;
}
static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
.vidioc_querycap = gsc_m2m_querycap,
.vidioc_enum_fmt_vid_cap_mplane = gsc_m2m_enum_fmt_mplane,
.vidioc_enum_fmt_vid_out_mplane = gsc_m2m_enum_fmt_mplane,
.vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane,
.vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane,
.vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane,
.vidioc_try_fmt_vid_out_mplane = gsc_m2m_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane,
.vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane,
.vidioc_reqbufs = gsc_m2m_reqbufs,
.vidioc_expbuf = gsc_m2m_expbuf,
.vidioc_querybuf = gsc_m2m_querybuf,
.vidioc_qbuf = gsc_m2m_qbuf,
.vidioc_dqbuf = gsc_m2m_dqbuf,
.vidioc_streamon = gsc_m2m_streamon,
.vidioc_streamoff = gsc_m2m_streamoff,
.vidioc_g_selection = gsc_m2m_g_selection,
.vidioc_s_selection = gsc_m2m_s_selection
};
static int queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq)
{
struct gsc_ctx *ctx = priv;
int ret;
memset(src_vq, 0, sizeof(*src_vq));
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->ops = &gsc_m2m_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret)
return ret;
memset(dst_vq, 0, sizeof(*dst_vq));
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->ops = &gsc_m2m_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
static int gsc_m2m_open(struct file *file)
{
struct gsc_dev *gsc = video_drvdata(file);
struct gsc_ctx *ctx = NULL;
int ret;
pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
if (mutex_lock_interruptible(&gsc->lock))
return -ERESTARTSYS;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
ret = -ENOMEM;
goto unlock;
}
v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
ret = gsc_ctrls_create(ctx);
if (ret)
goto error_fh;
/* Use separate control handler per file handle */
ctx->fh.ctrl_handler = &ctx->ctrl_handler;
file->private_data = &ctx->fh;
v4l2_fh_add(&ctx->fh);
ctx->gsc_dev = gsc;
/* Default color format */
ctx->s_frame.fmt = get_format(0);
ctx->d_frame.fmt = get_format(0);
/* Setup the device context for mem2mem mode. */
ctx->state = GSC_CTX_M2M;
ctx->flags = 0;
ctx->in_path = GSC_DMA;
ctx->out_path = GSC_DMA;
ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
pr_err("Failed to initialize m2m context");
ret = PTR_ERR(ctx->m2m_ctx);
goto error_ctrls;
}
if (gsc->m2m.refcnt++ == 0)
set_bit(ST_M2M_OPEN, &gsc->state);
pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx);
mutex_unlock(&gsc->lock);
return 0;
error_ctrls:
gsc_ctrls_delete(ctx);
error_fh:
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
kfree(ctx);
unlock:
mutex_unlock(&gsc->lock);
return ret;
}
static int gsc_m2m_release(struct file *file)
{
struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
struct gsc_dev *gsc = ctx->gsc_dev;
pr_debug("pid: %d, state: 0x%lx, refcnt= %d",
task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
mutex_lock(&gsc->lock);
v4l2_m2m_ctx_release(ctx->m2m_ctx);
gsc_ctrls_delete(ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
if (--gsc->m2m.refcnt <= 0)
clear_bit(ST_M2M_OPEN, &gsc->state);
kfree(ctx);
mutex_unlock(&gsc->lock);
return 0;
}
static unsigned int gsc_m2m_poll(struct file *file,
struct poll_table_struct *wait)
{
struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
struct gsc_dev *gsc = ctx->gsc_dev;
int ret;
if (mutex_lock_interruptible(&gsc->lock))
return -ERESTARTSYS;
ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
mutex_unlock(&gsc->lock);
return ret;
}
static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
{
struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
struct gsc_dev *gsc = ctx->gsc_dev;
int ret;
if (mutex_lock_interruptible(&gsc->lock))
return -ERESTARTSYS;
ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
mutex_unlock(&gsc->lock);
return ret;
}
static const struct v4l2_file_operations gsc_m2m_fops = {
.owner = THIS_MODULE,
.open = gsc_m2m_open,
.release = gsc_m2m_release,
.poll = gsc_m2m_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = gsc_m2m_mmap,
};
static struct v4l2_m2m_ops gsc_m2m_ops = {
.device_run = gsc_m2m_device_run,
.job_abort = gsc_m2m_job_abort,
};
int gsc_register_m2m_device(struct gsc_dev *gsc)
{
struct platform_device *pdev;
int ret;
if (!gsc)
return -ENODEV;
pdev = gsc->pdev;
gsc->vdev.fops = &gsc_m2m_fops;
gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops;
gsc->vdev.release = video_device_release_empty;
gsc->vdev.lock = &gsc->lock;
gsc->vdev.vfl_dir = VFL_DIR_M2M;
gsc->vdev.v4l2_dev = &gsc->v4l2_dev;
snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
GSC_MODULE_NAME, gsc->id);
video_set_drvdata(&gsc->vdev, gsc);
gsc->m2m.vfd = &gsc->vdev;
gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
if (IS_ERR(gsc->m2m.m2m_dev)) {
dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
ret = PTR_ERR(gsc->m2m.m2m_dev);
goto err_m2m_r1;
}
ret = video_register_device(&gsc->vdev, VFL_TYPE_GRABBER, -1);
if (ret) {
dev_err(&pdev->dev,
"%s(): failed to register video device\n", __func__);
goto err_m2m_r2;
}
pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);
return 0;
err_m2m_r2:
v4l2_m2m_release(gsc->m2m.m2m_dev);
err_m2m_r1:
video_device_release(gsc->m2m.vfd);
return ret;
}
void gsc_unregister_m2m_device(struct gsc_dev *gsc)
{
if (gsc)
v4l2_m2m_release(gsc->m2m.m2m_dev);
}

View file

@ -0,0 +1,430 @@
/*
* Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung EXYNOS5 SoC series G-Scaler driver
*
* 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.
*/
#include <linux/io.h>
#include <linux/delay.h>
#include "gsc-core.h"
void gsc_hw_set_sw_reset(struct gsc_dev *dev)
{
writel(GSC_SW_RESET_SRESET, dev->regs + GSC_SW_RESET);
}
int gsc_wait_reset(struct gsc_dev *dev)
{
unsigned long end = jiffies + msecs_to_jiffies(50);
u32 cfg;
while (time_before(jiffies, end)) {
cfg = readl(dev->regs + GSC_SW_RESET);
if (!cfg)
return 0;
usleep_range(10, 20);
}
return -EBUSY;
}
void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask)
{
u32 cfg;
cfg = readl(dev->regs + GSC_IRQ);
if (mask)
cfg |= GSC_IRQ_FRMDONE_MASK;
else
cfg &= ~GSC_IRQ_FRMDONE_MASK;
writel(cfg, dev->regs + GSC_IRQ);
}
void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask)
{
u32 cfg;
cfg = readl(dev->regs + GSC_IRQ);
if (mask)
cfg |= GSC_IRQ_ENABLE;
else
cfg &= ~GSC_IRQ_ENABLE;
writel(cfg, dev->regs + GSC_IRQ);
}
void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift,
bool enable)
{
u32 cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
u32 mask = 1 << shift;
cfg &= ~mask;
cfg |= enable << shift;
writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
}
void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
bool enable)
{
u32 cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
u32 mask = 1 << shift;
cfg &= ~mask;
cfg |= enable << shift;
writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
}
void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
int index)
{
pr_debug("src_buf[%d]: %pad, cb: %pad, cr: %pad", index,
&addr->y, &addr->cb, &addr->cr);
writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
}
void gsc_hw_set_output_addr(struct gsc_dev *dev,
struct gsc_addr *addr, int index)
{
pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad",
index, &addr->y, &addr->cb, &addr->cr);
writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
}
void gsc_hw_set_input_path(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
u32 cfg = readl(dev->regs + GSC_IN_CON);
cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
if (ctx->in_path == GSC_DMA)
cfg |= GSC_IN_PATH_MEMORY;
writel(cfg, dev->regs + GSC_IN_CON);
}
void gsc_hw_set_in_size(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_frame *frame = &ctx->s_frame;
u32 cfg;
/* Set input pixel offset */
cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left);
cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top);
writel(cfg, dev->regs + GSC_SRCIMG_OFFSET);
/* Set input original size */
cfg = GSC_SRCIMG_WIDTH(frame->f_width);
cfg |= GSC_SRCIMG_HEIGHT(frame->f_height);
writel(cfg, dev->regs + GSC_SRCIMG_SIZE);
/* Set input cropped size */
cfg = GSC_CROPPED_WIDTH(frame->crop.width);
cfg |= GSC_CROPPED_HEIGHT(frame->crop.height);
writel(cfg, dev->regs + GSC_CROPPED_SIZE);
}
void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_frame *frame = &ctx->s_frame;
u32 cfg;
cfg = readl(dev->regs + GSC_IN_CON);
if (frame->colorspace == V4L2_COLORSPACE_REC709)
cfg |= GSC_IN_RGB_HD_WIDE;
else
cfg |= GSC_IN_RGB_SD_WIDE;
if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
cfg |= GSC_IN_RGB565;
else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
cfg |= GSC_IN_XRGB8888;
writel(cfg, dev->regs + GSC_IN_CON);
}
void gsc_hw_set_in_image_format(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_frame *frame = &ctx->s_frame;
u32 i, depth = 0;
u32 cfg;
cfg = readl(dev->regs + GSC_IN_CON);
cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK |
GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK |
GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE);
writel(cfg, dev->regs + GSC_IN_CON);
if (is_rgb(frame->fmt->color)) {
gsc_hw_set_in_image_rgb(ctx);
return;
}
for (i = 0; i < frame->fmt->num_planes; i++)
depth += frame->fmt->depth[i];
switch (frame->fmt->num_comp) {
case 1:
cfg |= GSC_IN_YUV422_1P;
if (frame->fmt->yorder == GSC_LSB_Y)
cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y;
else
cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C;
if (frame->fmt->corder == GSC_CBCR)
cfg |= GSC_IN_CHROMA_ORDER_CBCR;
else
cfg |= GSC_IN_CHROMA_ORDER_CRCB;
break;
case 2:
if (depth == 12)
cfg |= GSC_IN_YUV420_2P;
else
cfg |= GSC_IN_YUV422_2P;
if (frame->fmt->corder == GSC_CBCR)
cfg |= GSC_IN_CHROMA_ORDER_CBCR;
else
cfg |= GSC_IN_CHROMA_ORDER_CRCB;
break;
case 3:
if (depth == 12)
cfg |= GSC_IN_YUV420_3P;
else
cfg |= GSC_IN_YUV422_3P;
break;
}
if (is_tiled(frame->fmt))
cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE;
writel(cfg, dev->regs + GSC_IN_CON);
}
void gsc_hw_set_output_path(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
u32 cfg = readl(dev->regs + GSC_OUT_CON);
cfg &= ~GSC_OUT_PATH_MASK;
if (ctx->out_path == GSC_DMA)
cfg |= GSC_OUT_PATH_MEMORY;
else
cfg |= GSC_OUT_PATH_LOCAL;
writel(cfg, dev->regs + GSC_OUT_CON);
}
void gsc_hw_set_out_size(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_frame *frame = &ctx->d_frame;
u32 cfg;
/* Set output original size */
if (ctx->out_path == GSC_DMA) {
cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left);
cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top);
writel(cfg, dev->regs + GSC_DSTIMG_OFFSET);
cfg = GSC_DSTIMG_WIDTH(frame->f_width);
cfg |= GSC_DSTIMG_HEIGHT(frame->f_height);
writel(cfg, dev->regs + GSC_DSTIMG_SIZE);
}
/* Set output scaled size */
if (ctx->gsc_ctrls.rotate->val == 90 ||
ctx->gsc_ctrls.rotate->val == 270) {
cfg = GSC_SCALED_WIDTH(frame->crop.height);
cfg |= GSC_SCALED_HEIGHT(frame->crop.width);
} else {
cfg = GSC_SCALED_WIDTH(frame->crop.width);
cfg |= GSC_SCALED_HEIGHT(frame->crop.height);
}
writel(cfg, dev->regs + GSC_SCALED_SIZE);
}
void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_frame *frame = &ctx->d_frame;
u32 cfg;
cfg = readl(dev->regs + GSC_OUT_CON);
if (frame->colorspace == V4L2_COLORSPACE_REC709)
cfg |= GSC_OUT_RGB_HD_WIDE;
else
cfg |= GSC_OUT_RGB_SD_WIDE;
if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
cfg |= GSC_OUT_RGB565;
else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
cfg |= GSC_OUT_XRGB8888;
writel(cfg, dev->regs + GSC_OUT_CON);
}
void gsc_hw_set_out_image_format(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_frame *frame = &ctx->d_frame;
u32 i, depth = 0;
u32 cfg;
cfg = readl(dev->regs + GSC_OUT_CON);
cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK |
GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK |
GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE);
writel(cfg, dev->regs + GSC_OUT_CON);
if (is_rgb(frame->fmt->color)) {
gsc_hw_set_out_image_rgb(ctx);
return;
}
if (ctx->out_path != GSC_DMA) {
cfg |= GSC_OUT_YUV444;
goto end_set;
}
for (i = 0; i < frame->fmt->num_planes; i++)
depth += frame->fmt->depth[i];
switch (frame->fmt->num_comp) {
case 1:
cfg |= GSC_OUT_YUV422_1P;
if (frame->fmt->yorder == GSC_LSB_Y)
cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y;
else
cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C;
if (frame->fmt->corder == GSC_CBCR)
cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
else
cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
break;
case 2:
if (depth == 12)
cfg |= GSC_OUT_YUV420_2P;
else
cfg |= GSC_OUT_YUV422_2P;
if (frame->fmt->corder == GSC_CBCR)
cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
else
cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
break;
case 3:
cfg |= GSC_OUT_YUV420_3P;
break;
}
if (is_tiled(frame->fmt))
cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE;
end_set:
writel(cfg, dev->regs + GSC_OUT_CON);
}
void gsc_hw_set_prescaler(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_scaler *sc = &ctx->scaler;
u32 cfg;
cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor);
cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio);
cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio);
writel(cfg, dev->regs + GSC_PRE_SCALE_RATIO);
}
void gsc_hw_set_mainscaler(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_scaler *sc = &ctx->scaler;
u32 cfg;
cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio);
writel(cfg, dev->regs + GSC_MAIN_H_RATIO);
cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio);
writel(cfg, dev->regs + GSC_MAIN_V_RATIO);
}
void gsc_hw_set_rotation(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
u32 cfg;
cfg = readl(dev->regs + GSC_IN_CON);
cfg &= ~GSC_IN_ROT_MASK;
switch (ctx->gsc_ctrls.rotate->val) {
case 270:
cfg |= GSC_IN_ROT_270;
break;
case 180:
cfg |= GSC_IN_ROT_180;
break;
case 90:
if (ctx->gsc_ctrls.hflip->val)
cfg |= GSC_IN_ROT_90_XFLIP;
else if (ctx->gsc_ctrls.vflip->val)
cfg |= GSC_IN_ROT_90_YFLIP;
else
cfg |= GSC_IN_ROT_90;
break;
case 0:
if (ctx->gsc_ctrls.hflip->val)
cfg |= GSC_IN_ROT_XFLIP;
else if (ctx->gsc_ctrls.vflip->val)
cfg |= GSC_IN_ROT_YFLIP;
}
writel(cfg, dev->regs + GSC_IN_CON);
}
void gsc_hw_set_global_alpha(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
struct gsc_frame *frame = &ctx->d_frame;
u32 cfg;
if (!is_rgb(frame->fmt->color)) {
pr_debug("Not a RGB format");
return;
}
cfg = readl(dev->regs + GSC_OUT_CON);
cfg &= ~GSC_OUT_GLOBAL_ALPHA_MASK;
cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val);
writel(cfg, dev->regs + GSC_OUT_CON);
}
void gsc_hw_set_sfr_update(struct gsc_ctx *ctx)
{
struct gsc_dev *dev = ctx->gsc_dev;
u32 cfg;
cfg = readl(dev->regs + GSC_ENABLE);
cfg |= GSC_ENABLE_SFR_UPDATE;
writel(cfg, dev->regs + GSC_ENABLE);
}

View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Register definition file for Samsung G-Scaler driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef REGS_GSC_H_
#define REGS_GSC_H_
/* G-Scaler enable */
#define GSC_ENABLE 0x00
#define GSC_ENABLE_OP_STATUS (1 << 2)
#define GSC_ENABLE_SFR_UPDATE (1 << 1)
#define GSC_ENABLE_ON (1 << 0)
/* G-Scaler S/W reset */
#define GSC_SW_RESET 0x04
#define GSC_SW_RESET_SRESET (1 << 0)
/* G-Scaler IRQ */
#define GSC_IRQ 0x08
#define GSC_IRQ_STATUS_OR_IRQ (1 << 17)
#define GSC_IRQ_STATUS_FRM_DONE_IRQ (1 << 16)
#define GSC_IRQ_FRMDONE_MASK (1 << 1)
#define GSC_IRQ_ENABLE (1 << 0)
/* G-Scaler input control */
#define GSC_IN_CON 0x10
#define GSC_IN_ROT_MASK (7 << 16)
#define GSC_IN_ROT_270 (7 << 16)
#define GSC_IN_ROT_90_YFLIP (6 << 16)
#define GSC_IN_ROT_90_XFLIP (5 << 16)
#define GSC_IN_ROT_90 (4 << 16)
#define GSC_IN_ROT_180 (3 << 16)
#define GSC_IN_ROT_YFLIP (2 << 16)
#define GSC_IN_ROT_XFLIP (1 << 16)
#define GSC_IN_RGB_TYPE_MASK (3 << 14)
#define GSC_IN_RGB_HD_NARROW (3 << 14)
#define GSC_IN_RGB_HD_WIDE (2 << 14)
#define GSC_IN_RGB_SD_NARROW (1 << 14)
#define GSC_IN_RGB_SD_WIDE (0 << 14)
#define GSC_IN_YUV422_1P_ORDER_MASK (1 << 13)
#define GSC_IN_YUV422_1P_ORDER_LSB_Y (0 << 13)
#define GSC_IN_YUV422_1P_OEDER_LSB_C (1 << 13)
#define GSC_IN_CHROMA_ORDER_MASK (1 << 12)
#define GSC_IN_CHROMA_ORDER_CBCR (0 << 12)
#define GSC_IN_CHROMA_ORDER_CRCB (1 << 12)
#define GSC_IN_FORMAT_MASK (7 << 8)
#define GSC_IN_XRGB8888 (0 << 8)
#define GSC_IN_RGB565 (1 << 8)
#define GSC_IN_YUV420_2P (2 << 8)
#define GSC_IN_YUV420_3P (3 << 8)
#define GSC_IN_YUV422_1P (4 << 8)
#define GSC_IN_YUV422_2P (5 << 8)
#define GSC_IN_YUV422_3P (6 << 8)
#define GSC_IN_TILE_TYPE_MASK (1 << 4)
#define GSC_IN_TILE_C_16x8 (0 << 4)
#define GSC_IN_TILE_MODE (1 << 3)
#define GSC_IN_LOCAL_SEL_MASK (3 << 1)
#define GSC_IN_PATH_MASK (1 << 0)
#define GSC_IN_PATH_MEMORY (0 << 0)
/* G-Scaler source image size */
#define GSC_SRCIMG_SIZE 0x14
#define GSC_SRCIMG_HEIGHT(x) ((x) << 16)
#define GSC_SRCIMG_WIDTH(x) ((x) << 0)
/* G-Scaler source image offset */
#define GSC_SRCIMG_OFFSET 0x18
#define GSC_SRCIMG_OFFSET_Y(x) ((x) << 16)
#define GSC_SRCIMG_OFFSET_X(x) ((x) << 0)
/* G-Scaler cropped source image size */
#define GSC_CROPPED_SIZE 0x1c
#define GSC_CROPPED_HEIGHT(x) ((x) << 16)
#define GSC_CROPPED_WIDTH(x) ((x) << 0)
/* G-Scaler output control */
#define GSC_OUT_CON 0x20
#define GSC_OUT_GLOBAL_ALPHA_MASK (0xff << 24)
#define GSC_OUT_GLOBAL_ALPHA(x) ((x) << 24)
#define GSC_OUT_RGB_TYPE_MASK (3 << 10)
#define GSC_OUT_RGB_HD_WIDE (3 << 10)
#define GSC_OUT_RGB_HD_NARROW (2 << 10)
#define GSC_OUT_RGB_SD_WIDE (1 << 10)
#define GSC_OUT_RGB_SD_NARROW (0 << 10)
#define GSC_OUT_YUV422_1P_ORDER_MASK (1 << 9)
#define GSC_OUT_YUV422_1P_ORDER_LSB_Y (0 << 9)
#define GSC_OUT_YUV422_1P_OEDER_LSB_C (1 << 9)
#define GSC_OUT_CHROMA_ORDER_MASK (1 << 8)
#define GSC_OUT_CHROMA_ORDER_CBCR (0 << 8)
#define GSC_OUT_CHROMA_ORDER_CRCB (1 << 8)
#define GSC_OUT_FORMAT_MASK (7 << 4)
#define GSC_OUT_XRGB8888 (0 << 4)
#define GSC_OUT_RGB565 (1 << 4)
#define GSC_OUT_YUV420_2P (2 << 4)
#define GSC_OUT_YUV420_3P (3 << 4)
#define GSC_OUT_YUV422_1P (4 << 4)
#define GSC_OUT_YUV422_2P (5 << 4)
#define GSC_OUT_YUV444 (7 << 4)
#define GSC_OUT_TILE_TYPE_MASK (1 << 2)
#define GSC_OUT_TILE_C_16x8 (0 << 2)
#define GSC_OUT_TILE_MODE (1 << 1)
#define GSC_OUT_PATH_MASK (1 << 0)
#define GSC_OUT_PATH_LOCAL (1 << 0)
#define GSC_OUT_PATH_MEMORY (0 << 0)
/* G-Scaler scaled destination image size */
#define GSC_SCALED_SIZE 0x24
#define GSC_SCALED_HEIGHT(x) ((x) << 16)
#define GSC_SCALED_WIDTH(x) ((x) << 0)
/* G-Scaler pre scale ratio */
#define GSC_PRE_SCALE_RATIO 0x28
#define GSC_PRESC_SHFACTOR(x) ((x) << 28)
#define GSC_PRESC_V_RATIO(x) ((x) << 16)
#define GSC_PRESC_H_RATIO(x) ((x) << 0)
/* G-Scaler main scale horizontal ratio */
#define GSC_MAIN_H_RATIO 0x2c
#define GSC_MAIN_H_RATIO_VALUE(x) ((x) << 0)
/* G-Scaler main scale vertical ratio */
#define GSC_MAIN_V_RATIO 0x30
#define GSC_MAIN_V_RATIO_VALUE(x) ((x) << 0)
/* G-Scaler destination image size */
#define GSC_DSTIMG_SIZE 0x40
#define GSC_DSTIMG_HEIGHT(x) ((x) << 16)
#define GSC_DSTIMG_WIDTH(x) ((x) << 0)
/* G-Scaler destination image offset */
#define GSC_DSTIMG_OFFSET 0x44
#define GSC_DSTIMG_OFFSET_Y(x) ((x) << 16)
#define GSC_DSTIMG_OFFSET_X(x) ((x) << 0)
/* G-Scaler input y address mask */
#define GSC_IN_BASE_ADDR_Y_MASK 0x4c
/* G-Scaler input y base address */
#define GSC_IN_BASE_ADDR_Y(n) (0x50 + (n) * 0x4)
/* G-Scaler input cb address mask */
#define GSC_IN_BASE_ADDR_CB_MASK 0x7c
/* G-Scaler input cb base address */
#define GSC_IN_BASE_ADDR_CB(n) (0x80 + (n) * 0x4)
/* G-Scaler input cr address mask */
#define GSC_IN_BASE_ADDR_CR_MASK 0xac
/* G-Scaler input cr base address */
#define GSC_IN_BASE_ADDR_CR(n) (0xb0 + (n) * 0x4)
/* G-Scaler output y address mask */
#define GSC_OUT_BASE_ADDR_Y_MASK 0x10c
/* G-Scaler output y base address */
#define GSC_OUT_BASE_ADDR_Y(n) (0x110 + (n) * 0x4)
/* G-Scaler output cb address mask */
#define GSC_OUT_BASE_ADDR_CB_MASK 0x15c
/* G-Scaler output cb base address */
#define GSC_OUT_BASE_ADDR_CB(n) (0x160 + (n) * 0x4)
/* G-Scaler output cr address mask */
#define GSC_OUT_BASE_ADDR_CR_MASK 0x1ac
/* G-Scaler output cr base address */
#define GSC_OUT_BASE_ADDR_CR(n) (0x1b0 + (n) * 0x4)
#endif /* REGS_GSC_H_ */

View file

@ -0,0 +1,26 @@
#
# Exynos multimedia device drivers
#
config VIDEO_EXYNOS
bool "Exynos Multimedia Devices"
depends on ARCH_EXYNOS
default y
help
This is a representative exynos multimedia device.
if VIDEO_EXYNOS
source "drivers/media/platform/exynos/mdev/Kconfig"
source "drivers/media/platform/exynos/scaler/Kconfig"
source "drivers/media/platform/exynos/mfc/Kconfig"
source "drivers/media/platform/exynos/hevc/Kconfig"
source "drivers/media/platform/exynos/fimg2d_v5/Kconfig"
source "drivers/media/platform/exynos/smfc/Kconfig"
source "drivers/media/platform/exynos/fimc-is2/Kconfig"
endif
config MEDIA_EXYNOS
bool
depends on VIDEO_EXYNOS
default y
help
Compile mdev to use exynos media device driver.

View file

@ -0,0 +1,8 @@
obj-$(CONFIG_EXYNOS_MEDIA_DEVICE) += mdev/
obj-$(CONFIG_VIDEO_EXYNOS_SCALER) += scaler/
obj-$(CONFIG_VIDEO_EXYNOS_MFC) += mfc/
obj-$(CONFIG_VIDEO_EXYNOS_HEVC) += hevc/
obj-$(CONFIG_VIDEO_EXYNOS_FIMG2D_V5) += fimg2d_v5/
obj-$(CONFIG_VIDEO_EXYNOS_SMFC) += smfc/
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_IS2) += fimc-is2/
EXTRA_CLAGS += -Idrivers/media/video

View file

@ -0,0 +1,20 @@
config VIDEO_EXYNOS_FIMC_IS2
bool "Exynos FIMC-IS2 (Image Subsystem) driver"
depends on VIDEO_EXYNOS
select MEDIA_EXYNOS
select EXYNOS_FIMC_IS
default n
help
This is a v4l2 driver for exynos FIMC-IS device.
source "drivers/media/platform/exynos/fimc-is2/ischain/Kconfig"
source "drivers/media/platform/exynos/fimc-is2/sensor/Kconfig"
#menu depth1
menu "Base Feature Setting"
depends on EXYNOS_FIMC_IS
source "drivers/media/platform/exynos/fimc-is2/hardware/Kconfig"
endmenu
source "drivers/media/platform/exynos/fimc-is2/vendor/Kconfig"

View file

@ -0,0 +1,84 @@
fimc-is-objs := fimc-is-core.o \
fimc-is-spi.o \
fimc-is-i2c.o \
fimc-is-debug.o \
fimc-is-mem.o \
fimc-is-pipe.o \
fimc-is-framemgr.o \
fimc-is-groupmgr.o \
fimc-is-resourcemgr.o \
fimc-is-devicemgr.o \
fimc-is-video.o \
fimc-is-video-sensor.o \
fimc-is-video-3aa.o \
fimc-is-video-3aac.o \
fimc-is-video-3aap.o \
fimc-is-video-isp.o \
fimc-is-video-ispc.o \
fimc-is-video-ispp.o \
fimc-is-video-dis.o \
fimc-is-video-scc.o \
fimc-is-video-scp.o \
fimc-is-video-mcs.o \
fimc-is-video-mcsp.o \
fimc-is-video-vra.o \
fimc-is-video-ssvc0.o \
fimc-is-video-ssvc1.o \
fimc-is-video-ssvc2.o \
fimc-is-video-ssvc3.o \
fimc-is-subdev-ctrl.o \
fimc-is-device-flite.o \
fimc-is-device-ischain.o \
fimc-is-time.o \
fimc-is-dvfs.o \
fimc-is-dt.o \
fimc-is-clk-gate.o \
fimc-is-device-preprocessor.o \
fimc-is-video-preprocessor.o \
fimc-is-binary.o
ifeq ($(CONFIG_USE_DIRECT_IS_CONTROL),y)
fimc-is-objs += fimc-is-interface-wrap.o
else
fimc-is-objs += fimc-is-interface-wrap-fw.o
fimc-is-objs += fimc-is-interface.o
endif
ifeq ($(CONFIG_USE_SENSOR_GROUP),y)
fimc-is-objs += fimc-is-device-sensor_v2.o
else
fimc-is-objs += fimc-is-device-sensor.o
endif
obj-$(CONFIG_EXYNOS_DEVICE_MIPI_CSIS) += fimc-is-device-csi.o
obj-$(CONFIG_EXYNOS_DEVICE_MIPI_CSIS_VER2) += fimc-is-device-csi_v2.o
obj-$(CONFIG_EXYNOS_DEVICE_MIPI_CSIS_VER3) += fimc-is-device-csi_v3.o
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_IS2) += fimc-is.o
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_IS2) += sensor/
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_IS2) += ischain/
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_IS2) += vendor/
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_IS2) += hardware/
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_IS2) += interface/
obj-$(CONFIG_USE_DIRECT_IS_CONTROL) += hardware/
obj-$(CONFIG_USE_DIRECT_IS_CONTROL) += interface/
subdir-ccflags-y := -Idrivers/media/platform/exynos/fimc-is2
subdir-ccflags-y += -Idrivers/media/platform/exynos/fimc-is2/include
subdir-ccflags-y += -Idrivers/media/platform/exynos/fimc-is2/interface
subdir-ccflags-y += -Idrivers/media/platform/exynos/fimc-is2/hardware
subdir-ccflags-y += -Idrivers/media/platform/exynos/fimc-is2/hardware/api
subdir-ccflags-y += -Idrivers/media/platform/exynos/fimc-is2/vendor
subdir-ccflags-y += -Idrivers/media/platform/exynos/fimc-is2/sensor/csi
subdir-ccflags-$(CONFIG_FIMC_IS_V3_1_1) += -Idrivers/media/platform/exynos/fimc-is2/ischain/fimc-is-v3_1_1
subdir-ccflags-$(CONFIG_FIMC_IS_V3_11_0) += -Idrivers/media/platform/exynos/fimc-is2/ischain/fimc-is-v3_11_0
subdir-ccflags-$(CONFIG_FIMC_IS_V3_20_0) += -Idrivers/media/platform/exynos/fimc-is2/ischain/fimc-is-v3_20_0
subdir-ccflags-$(CONFIG_FIMC_IS_V4_0_0) += -Idrivers/media/platform/exynos/fimc-is2/ischain/fimc-is-v4_0_0
subdir-ccflags-$(CONFIG_FIMC_IS_V4_3_0) += -Idrivers/media/platform/exynos/fimc-is2/ischain/fimc-is-v4_3_0
subdir-ccflags-$(CONFIG_FIMC_IS_V5_10_0) += -Idrivers/media/platform/exynos/fimc-is2/ischain/fimc-is-v5_10_0
ifeq ($(CONFIG_VENDER_MCD),y)
subdir-ccflags-y += -Idrivers/media/platform/exynos/fimc-is2/vendor/mcd
else
subdir-ccflags-y += -Idrivers/media/platform/exynos/fimc-is2/vendor/default
endif

View file

@ -0,0 +1,254 @@
/*
* Samsung EXYNOS FIMC-IS (Imaging Subsystem) driver
*
* Copyright (C) 2015 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/device.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/firmware.h>
#include "fimc-is-binary.h"
static noinline_for_stack long get_file_size(struct file *file)
{
struct kstat st;
if (vfs_getattr(&file->f_path, &st))
return -1;
if (!S_ISREG(st.mode))
return -1;
if (st.size != (long)st.size)
return -1;
return st.size;
}
static int read_file_contents(struct file *fp, struct fimc_is_binary *bin)
{
long size;
char *buf;
int ret;
size = get_file_size(fp);
if (size <= 0)
return -EBADF;
/* if a buffer for binary is already allocated */
if (bin->data) {
if (!bin->size)
return -EINVAL;
buf = bin->data;
/* shrink read size to fit the size of a given buffer */
if (size > bin->size) {
WARN_ON(1);
size = bin->size;
}
} else {
buf = bin->alloc(size);
if (!buf)
return -ENOMEM;
}
ret = kernel_read(fp, 0, buf, size);
if (ret != size) {
if (!bin->data)
bin->free(buf);
return ret;
}
bin->data = buf;
bin->size = size;
return 0;
}
static int write_file_contents(struct file *fp, struct fimc_is_binary *bin)
{
ssize_t ret;
ssize_t count;
loff_t pos = 0;
char *buf = (char *)bin->data;
do {
count = min_t(ssize_t, PAGE_SIZE, bin->size - pos);
ret = kernel_write(fp, buf, count, pos);
if (ret > 0) {
pos += ret;
buf += ret;
} else {
break;
}
} while (pos < bin->size);
return 0;
}
/**
* get_filesystem_binary: copy a binary in userland to a given buffer
* @filename: filenam of binary file including full path
* @bin: pointer to fimc_is_binary structure
**/
int get_filesystem_binary(const char *filename, struct fimc_is_binary *bin)
{
struct file *fp;
int ret = 0;
fp = filp_open(filename, O_RDONLY, 0);
if (!IS_ERR_OR_NULL(fp)) {
ret = read_file_contents(fp, bin);
fput(fp);
} else {
ret = PTR_ERR(fp);
}
return ret;
}
/**
* put_filesystem_binary: copy a given buffer to a file in userland
* @filename: filenam of binary file including full path
* @bin: pointer to fimc_is_binary structure
**/
int put_filesystem_binary(const char *filename, struct fimc_is_binary *bin, u32 flags)
{
struct file *fp;
int ret = 0;
fp = filp_open(filename, flags, 0666);
if (!IS_ERR_OR_NULL(fp)) {
ret = write_file_contents(fp, bin);
fput(fp);
} else {
ret = PTR_ERR(fp);
}
return ret;
}
/**
* setup_binary_loader: customize an instance of the binary loader
* @bin: pointer to fimc_is_binary structure
* @retry_cnt: retry count of request_firmware
* @retry_err: specific error number for retry of request_firmware
* @alloc: custom allocation function for file system loading
* @free: custom free function for file system loading
**/
void setup_binary_loader(struct fimc_is_binary *bin,
unsigned int retry_cnt, int retry_err,
void *(*alloc)(unsigned long size),
void (*free)(const void *buf))
{
bin->retry_cnt = retry_cnt;
bin->retry_err = retry_err;
if (alloc && free) {
bin->alloc = alloc;
bin->free = free;
} else {
/* set vmalloc/vfree as a default */
bin->alloc = &vmalloc;
bin->free = &vfree;
}
bin->customized = (unsigned long)bin;
}
/**
* request_binary: send loading request to the loader
* @bin: pointer to fimc_is_binary structure
* @path: path of binary file
* @name: name of binary file
* @device: device for which binary is being loaded
**/
int request_binary(struct fimc_is_binary *bin, const char *path,
const char *name, struct device *device)
{
char *filename;
unsigned int retry_cnt = 0;
int retry_err = 0;
int ret;
bin->data = NULL;
bin->size = 0;
bin->fw = NULL;
/* whether the loader is customized or not */
if (bin->customized != (unsigned long)bin) {
bin->alloc = &vmalloc;
bin->free = &vfree;
} else {
retry_cnt = bin->retry_cnt;
retry_err = bin->retry_err;
}
/* read the requested binary from file system directly */
if (path) {
filename = __getname();
if (unlikely(!filename))
return -ENOMEM;
snprintf(filename, PATH_MAX, "%s%s", path, name);
ret = get_filesystem_binary(filename, bin);
__putname(filename);
/* read successfully or don't want to go further more */
if (!ret || !device)
return ret;
}
/* ask to 'request_firmware' */
do {
ret = request_firmware(&bin->fw, name, device);
if (!ret && bin->fw) {
bin->data = (void *)bin->fw->data;
bin->size = bin->fw->size;
break;
}
/*
* if no specific error for retry is given;
* whatever any error is occurred, we should retry
*/
if (!bin->retry_err)
retry_err = ret;
} while (retry_cnt-- && (retry_err == ret));
return ret;
}
/**
* release_firmware: release the resource related to a binary
* @bin: binary resource to release
**/
void release_binary(struct fimc_is_binary *bin)
{
if (bin->fw)
release_firmware(bin->fw);
else if (bin->data)
bin->free(bin->data);
}
/**
* was_loaded_by: how was a binary loaded
* @bin: pointer to fimc_is_binary structure
**/
int was_loaded_by(struct fimc_is_binary *bin)
{
if (bin->fw)
return 1; /* by request_firmware */
else if (bin->data)
return 0; /* from file system */
else
return -EINVAL;
}

View file

@ -0,0 +1,261 @@
/*
* Samsung Exynos SoC series FIMC-IS driver
*
* exynos fimc-is core functions
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "fimc-is-clk-gate.h"
#include "fimc-is-regs.h"
int fimc_is_clk_gate_init(struct fimc_is_core *core)
{
struct fimc_is_clk_gate_ctrl *gate_ctrl;
pr_info("%s\n", __func__);
if (!core) {
err("core is NULL\n");
return -EINVAL;
}
gate_ctrl = &core->resourcemgr.clk_gate_ctrl;
memset(gate_ctrl, 0x0, sizeof(struct fimc_is_clk_gate_ctrl));
/* init spin_lock for clock gating */
spin_lock_init(&gate_ctrl->lock);
core->resourcemgr.clk_gate_ctrl.gate_info = core->pdata->gate_info;
/* ISSR53 is clock gating debugging region.
* High means clock on state.
* To prevent telling A5 wrong clock off state,
* clock on state should be set before clock off is set.
*/
writel(0xFFFFFFFF, core->ischain[0].interface->regs + ISSR53);
return 0;
}
int fimc_is_clk_gate_lock_set(struct fimc_is_core *core, u32 instance, u32 is_start)
{
spin_lock(&core->resourcemgr.clk_gate_ctrl.lock);
core->resourcemgr.clk_gate_ctrl.msk_lock_by_ischain[instance] = is_start;
spin_unlock(&core->resourcemgr.clk_gate_ctrl.lock);
return 0;
}
#if 0
/* This function may be used when clk_enable api will be faster than now */
int fimc_is_clk_gate_reg_set(struct fimc_is_core *core,
bool is_on, const char* gate_str, u32 clk_gate_id,
struct exynos_fimc_is_clk_gate_info *gate_info)
{
struct platform_device *pdev = core->pdev;
if (is_on) {
if (gate_info->clk_on(pdev, gate_str) < 0) {
pr_err("%s: could not enable %s\n", __func__, gate_str);
return -EINVAL;
}
} else {
if (gate_info->clk_off(pdev, gate_str) < 0) {
pr_err("%s: could not disable %s\n", __func__, gate_str);
return -EINVAL;
}
}
return 0;
}
#endif
int fimc_is_wrap_clk_gate_set(struct fimc_is_core *core,
int msk_group_id, bool is_on)
{
int i;
for (i = 0; i < FIMC_IS_GRP_MAX; i++) {
if (msk_group_id & (1 << i))
fimc_is_clk_gate_set(core, i, is_on, true, false);
}
return 0;
}
inline bool fimc_is_group_otf(struct fimc_is_device_ischain *device, int group_id)
{
struct fimc_is_group *group;
switch (group_id) {
case GROUP_ID_3AA0:
case GROUP_ID_3AA1:
group = &device->group_3aa;
break;
case GROUP_ID_ISP0:
case GROUP_ID_ISP1:
group = &device->group_isp;
break;
case GROUP_ID_DIS0:
group = &device->group_dis;
break;
default:
group = NULL;
pr_err("%s unresolved group id %d", __func__, group_id);
return false;
}
if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state))
return true;
else
return false;
}
int fimc_is_clk_gate_set(struct fimc_is_core *core,
int group_id, bool is_on, bool skip_set_state, bool user_scenario)
{
int ret = 0;
int cfg = 0;
int i;
struct fimc_is_clk_gate_ctrl *gate_ctrl;
struct exynos_fimc_is_clk_gate_info *gate_info;
u32 mask_on, mask_off, mask_cond_depend;
gate_ctrl = &core->resourcemgr.clk_gate_ctrl;
gate_info = gate_ctrl->gate_info;
pr_debug("%s in\n", __func__);
spin_lock(&gate_ctrl->lock);
/* Set State */
if (is_on) {
if (skip_set_state == false) {
(gate_ctrl->chk_on_off_cnt[group_id])++; /* for debuging */
(gate_ctrl->msk_cnt[group_id])++;
set_bit(group_id, &gate_ctrl->msk_state);
}
gate_info->groups[group_id].mask_clk_on_mod =
gate_info->groups[group_id].mask_clk_on_org;
} else {
(gate_ctrl->chk_on_off_cnt[group_id])--; /* for debuging */
(gate_ctrl->msk_cnt[group_id])--;
if ((gate_ctrl->msk_cnt[group_id]) < 0) {
pr_warn("%s msk_cnt[%d] is lower than zero !!\n", __func__, group_id);
(gate_ctrl->msk_cnt[group_id]) = 0;
}
if ((gate_ctrl->msk_cnt[group_id]) == 0)
clear_bit(group_id, &gate_ctrl->msk_state);
/* if there's some processing group shot, don't clock off */
if (test_bit_variables(group_id, &gate_ctrl->msk_state))
goto exit;
gate_info->groups[group_id].mask_clk_off_self_mod =
gate_info->groups[group_id].mask_clk_off_self_org;
}
/* Don't off!! when other instance opening/closing */
if (is_on == false) {
for (i = 0; i < FIMC_IS_STREAM_COUNT; i++) {
if ((!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &core->ischain[i].state)) &&
(gate_ctrl->msk_lock_by_ischain[i])) {
pr_info("%s lock(on) due to instance(%d)\n", __func__, i);
goto exit;
}
/* don't off! if there is at least this group that is OTF */
if (fimc_is_group_otf(&core->ischain[i], group_id)) {
pr_debug("%s don't off!! this instance(%d) group(%d) is OTF\n",
__func__, i, group_id);
goto exit;
}
}
}
/* Check user scenario */
if (user_scenario && gate_info->user_clk_gate) {
if (fimc_is_set_user_clk_gate(group_id,
core,
is_on,
gate_ctrl->msk_state,
gate_info) < 0) {
pr_debug("%s user scenario is skip!! [%d] !!\n", __func__, group_id);
goto exit;
}
}
/* Get the region for clock gating debug */
cfg = readl(core->ischain[0].interface->regs + ISSR53);
/* Get Mask of self-on/off */
if (is_on)
mask_on = gate_info->groups[group_id].mask_clk_on_mod;
else
mask_off = gate_info->groups[group_id].mask_clk_off_self_mod;
/* Clock on */
if (is_on && ((gate_ctrl->msk_clk_on_off_state) != mask_on)) {
cfg |= (mask_on << 16); /* shortly before clock on */
writel(cfg, core->ischain[0].interface->regs + ISSR53);
ret = gate_info->clk_on_off(mask_on, is_on);
gate_ctrl->msk_clk_on_off_state |= mask_on;
cfg |= (mask_on); /* after clock on */
writel(cfg, core->ischain[0].interface->regs + ISSR53);
}
/* Clock off and check dependancy (it's for only clock-off) */
if (is_on == false) {
mask_cond_depend = gate_info->groups[group_id].mask_cond_for_depend;
/* check dependancy */
if (mask_cond_depend > 0 &&
(mask_cond_depend & (gate_ctrl->msk_state))) {
mask_off |= gate_info->groups[group_id].mask_clk_off_depend;
}
/* clock off */
if (((gate_ctrl->msk_clk_on_off_state) & mask_off) > 0) {
cfg &= ~(mask_off << 16); /* shortly before clock off */
writel(cfg, core->ischain[0].interface->regs + ISSR53);
ret = gate_info->clk_on_off(mask_off, is_on);
gate_ctrl->msk_clk_on_off_state &= ~(mask_off);
cfg &= ~(mask_off); /* after clock off */
writel(cfg, core->ischain[0].interface->regs + ISSR53);
}
}
exit:
spin_unlock(&gate_ctrl->lock);
pr_debug("%s out\n", __func__);
return ret;
}
int fimc_is_set_user_clk_gate(u32 group_id,
struct fimc_is_core *core,
bool is_on,
unsigned long msk_state,
struct exynos_fimc_is_clk_gate_info *gate_info)
{
u32 user_scenario_id = 0;
/* deside what user scenario is */
#if defined(ENABLE_FULL_BYPASS)
if (group_id == GROUP_ID_ISP0)
user_scenario_id = CLK_GATE_FULL_BYPASS_SN;
#else
if (group_id == GROUP_ID_ISP0)
user_scenario_id = CLK_GATE_NOT_FULL_BYPASS_SN;
#endif
if (gate_info->user_clk_gate(group_id,
is_on,
user_scenario_id,
msk_state,
gate_info) < 0) {
pr_err("%s user_clk_gate failed(%d) !!\n", __func__, group_id);
}
return 0;
}

View file

@ -0,0 +1,40 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_CLK_GATE_H
#define FIMC_IS_CLK_GATE_H
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include "fimc-is-core.h"
int fimc_is_clk_gate_init(struct fimc_is_core *core);
int fimc_is_clk_gate_lock_set(struct fimc_is_core *core, u32 instance, u32 is_start);
int fimc_is_clk_gate_reg_set(struct fimc_is_core *core,
bool is_on, const char* gate_str, u32 clk_gate_id,
struct exynos_fimc_is_clk_gate_info *gate_info);
/* For several groups */
int fimc_is_wrap_clk_gate_set(struct fimc_is_core *core,
int msk_group_id, bool is_on);
/* For only single group */
int fimc_is_clk_gate_set(struct fimc_is_core *core,
int group_id, bool is_on, bool skip_set_state, bool user_scenario);
int fimc_is_set_user_clk_gate(u32 group_id,
struct fimc_is_core *core,
bool is_on,
unsigned long msk_state,
struct exynos_fimc_is_clk_gate_info *gate_info);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,303 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_CORE_H
#define FIMC_IS_CORE_H
#include <linux/version.h>
#include <linux/sched.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
#include <linux/sched/rt.h>
#endif
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
#if defined(CONFIG_VIDEOBUF2_CMA_PHYS)
#include <media/videobuf2-cma-phys.h>
#elif defined(CONFIG_VIDEOBUF2_ION)
#include <media/videobuf2-ion.h>
#endif
#include <exynos-fimc-is.h>
#include "fimc-is-interface.h"
#include "fimc-is-framemgr.h"
#include "fimc-is-resourcemgr.h"
#include "fimc-is-devicemgr.h"
#include "fimc-is-device-sensor.h"
#include "fimc-is-device-ischain.h"
#ifndef ENABLE_IS_CORE
#include "hardware/fimc-is-hw-control.h"
#include "interface/fimc-is-interface-ischain.h"
#endif
#include "fimc-is-device-preprocessor.h"
#include "fimc-is-spi.h"
#include "fimc-is-video.h"
#include "fimc-is-mem.h"
#include "fimc-is-vender.h"
#define FIMC_IS_DRV_NAME "exynos-fimc-is"
#define FIMC_IS_COMMAND_TIMEOUT (3*HZ)
#define FIMC_IS_STARTUP_TIMEOUT (3*HZ)
#define FIMC_IS_SHUTDOWN_TIMEOUT (10*HZ)
#define FIMC_IS_FLITE_STOP_TIMEOUT (3*HZ)
#define FIMC_IS_SENSOR_MAX_ENTITIES (1)
#define FIMC_IS_SENSOR_PAD_SOURCE_FRONT (0)
#define FIMC_IS_SENSOR_PADS_NUM (1)
#define FIMC_IS_FRONT_MAX_ENTITIES (1)
#define FIMC_IS_FRONT_PAD_SINK (0)
#define FIMC_IS_FRONT_PAD_SOURCE_BACK (1)
#define FIMC_IS_FRONT_PAD_SOURCE_BAYER (2)
#define FIMC_IS_FRONT_PAD_SOURCE_SCALERC (3)
#define FIMC_IS_FRONT_PADS_NUM (4)
#define FIMC_IS_BACK_MAX_ENTITIES (1)
#define FIMC_IS_BACK_PAD_SINK (0)
#define FIMC_IS_BACK_PAD_SOURCE_3DNR (1)
#define FIMC_IS_BACK_PAD_SOURCE_SCALERP (2)
#define FIMC_IS_BACK_PADS_NUM (3)
#define FIMC_IS_MAX_SENSOR_NAME_LEN (16)
#define MAX_ODC_INTERNAL_BUF_WIDTH (2560) /* 4808 in HW */
#define MAX_ODC_INTERNAL_BUF_HEIGHT (1920) /* 3356 in HW */
#define SIZE_ODC_INTERNAL_BUF \
(MAX_ODC_INTERNAL_BUF_WIDTH * MAX_ODC_INTERNAL_BUF_HEIGHT * 3)
#define MAX_DIS_INTERNAL_BUF_WIDTH (2400)
#define MAX_DIS_INTERNAL_BUF_HEIGHT (1360)
#define SIZE_DIS_INTERNAL_BUF \
(MAX_DIS_INTERNAL_BUF_WIDTH * MAX_DIS_INTERNAL_BUF_HEIGHT * 2)
#define MAX_3DNR_INTERNAL_BUF_WIDTH (3840)
#define MAX_3DNR_INTERNAL_BUF_HEIGHT (2160)
#ifdef TPU_COMPRESSOR
#define SIZE_DNR_INTERNAL_BUF \
ALIGN((MAX_3DNR_INTERNAL_BUF_WIDTH * MAX_3DNR_INTERNAL_BUF_HEIGHT * 22 / 2 / 8), 16)
#else
#define SIZE_DNR_INTERNAL_BUF \
ALIGN((MAX_3DNR_INTERNAL_BUF_WIDTH * MAX_3DNR_INTERNAL_BUF_HEIGHT * 18 / 8), 16)
#endif
#define NUM_LHFD_INTERNAL_BUF (3)
#define MAX_LHFD_INTERNEL_BUF_WIDTH (640)
#define MAX_LHFD_INTERNEL_BUF_HEIGHT (480)
#define MAX_LHFD_INTERNEL_BUF_SIZE \
(MAX_LHFD_INTERNEL_BUF_WIDTH * MAX_LHFD_INTERNEL_BUF_HEIGHT)
#define SIZE_LHFD_INTERNEL_BUF \
((MAX_LHFD_INTERNEL_BUF_SIZE * 45 / 4) + (4096 + 1024))
/*
* FD one buffer size: 3.4 MB
* FD_ map_data_1: MAX_FD_INTERNEL_BUF_SIZE * 3 / 2) byte
* FD_ map_data_2: MAX_FD_INTERNEL_BUF_SIZE * 4) byte
* FD_ map_data_3: MAX_FD_INTERNEL_BUF_SIZE * 4) byte
* FD_ map_data_4: MAX_FD_INTERNEL_BUF_SIZE / 4) byte
* FD_ map_data_5: MAX_FD_INTERNEL_BUF_SIZE) byte
* FD_ map_data_6: 1024 byte
* FD_ map_data_7: 256 byte
*/
#define SIZE_LHFD_SHOT_BUF sizeof(struct camera2_shot)
#define MAX_LHFD_SHOT_BUF (2)
#define NUM_VRA_INTERNAL_BUF (1)
#define SIZE_VRA_INTERNEL_BUF (0x00650000)
#define NUM_ODC_INTERNAL_BUF (2)
#define NUM_DIS_INTERNAL_BUF (1)
#define NUM_DNR_INTERNAL_BUF (2)
#define GATE_IP_ISP (0)
#define GATE_IP_DRC (1)
#define GATE_IP_FD (2)
#define GATE_IP_SCC (3)
#define GATE_IP_SCP (4)
#define GATE_IP_ODC (0)
#define GATE_IP_DIS (1)
#define GATE_IP_DNR (2)
#ifndef ENABLE_IS_CORE
/* use sysfs for actuator */
#define INIT_MAX_SETTING 5
#endif
enum fimc_is_debug_device {
FIMC_IS_DEBUG_MAIN = 0,
FIMC_IS_DEBUG_EC,
FIMC_IS_DEBUG_SENSOR,
FIMC_IS_DEBUG_ISP,
FIMC_IS_DEBUG_DRC,
FIMC_IS_DEBUG_FD,
FIMC_IS_DEBUG_SDK,
FIMC_IS_DEBUG_SCALERC,
FIMC_IS_DEBUG_ODC,
FIMC_IS_DEBUG_DIS,
FIMC_IS_DEBUG_TDNR,
FIMC_IS_DEBUG_SCALERP
};
enum fimc_is_debug_target {
FIMC_IS_DEBUG_UART = 0,
FIMC_IS_DEBUG_MEMORY,
FIMC_IS_DEBUG_DCC3
};
enum fimc_is_front_input_entity {
FIMC_IS_FRONT_INPUT_NONE = 0,
FIMC_IS_FRONT_INPUT_SENSOR,
};
enum fimc_is_front_output_entity {
FIMC_IS_FRONT_OUTPUT_NONE = 0,
FIMC_IS_FRONT_OUTPUT_BACK,
FIMC_IS_FRONT_OUTPUT_BAYER,
FIMC_IS_FRONT_OUTPUT_SCALERC,
};
enum fimc_is_back_input_entity {
FIMC_IS_BACK_INPUT_NONE = 0,
FIMC_IS_BACK_INPUT_FRONT,
};
enum fimc_is_back_output_entity {
FIMC_IS_BACK_OUTPUT_NONE = 0,
FIMC_IS_BACK_OUTPUT_3DNR,
FIMC_IS_BACK_OUTPUT_SCALERP,
};
enum fimc_is_front_state {
FIMC_IS_FRONT_ST_POWERED = 0,
FIMC_IS_FRONT_ST_STREAMING,
FIMC_IS_FRONT_ST_SUSPENDED,
};
enum fimc_is_clck_gate_mode {
CLOCK_GATE_MODE_HOST = 0,
CLOCK_GATE_MODE_FW,
};
struct fimc_is_sysfs_debug {
unsigned int en_dvfs;
unsigned int en_clk_gate;
unsigned int clk_gate_mode;
};
#ifndef ENABLE_IS_CORE
struct fimc_is_sysfs_actuator {
unsigned int init_step;
int init_positions[INIT_MAX_SETTING];
int init_delays[INIT_MAX_SETTING];
};
#endif
#ifdef FIXED_SENSOR_DEBUG
struct fimc_is_sysfs_sensor {
bool is_en;
unsigned int frame_duration;
unsigned int long_exposure_time;
unsigned int short_exposure_time;
unsigned int long_analog_gain;
unsigned int short_analog_gain;
unsigned int long_digital_gain;
unsigned int short_digital_gain;
};
#endif
struct fimc_is_core {
struct platform_device *pdev;
struct resource *regs_res;
void __iomem *regs;
int irq;
u32 current_position;
atomic_t rsccount;
unsigned long state;
/* depended on isp */
struct exynos_platform_fimc_is *pdata;
struct fimc_is_resourcemgr resourcemgr;
struct fimc_is_groupmgr groupmgr;
struct fimc_is_devicemgr devicemgr;
struct fimc_is_interface interface;
struct fimc_is_device_preproc preproc;
struct fimc_is_device_sensor sensor[FIMC_IS_SENSOR_COUNT];
struct fimc_is_device_ischain ischain[FIMC_IS_STREAM_COUNT];
#ifndef ENABLE_IS_CORE
struct fimc_is_hardware hardware;
struct fimc_is_interface_ischain interface_ischain;
#endif
struct v4l2_device v4l2_dev;
struct fimc_is_video video_30s;
struct fimc_is_video video_30c;
struct fimc_is_video video_30p;
struct fimc_is_video video_31s;
struct fimc_is_video video_31c;
struct fimc_is_video video_31p;
struct fimc_is_video video_i0s;
struct fimc_is_video video_i0c;
struct fimc_is_video video_i0p;
struct fimc_is_video video_i1s;
struct fimc_is_video video_i1c;
struct fimc_is_video video_i1p;
struct fimc_is_video video_scc;
struct fimc_is_video video_scp;
struct fimc_is_video video_vdc;
struct fimc_is_video video_vdo;
struct fimc_is_video video_m0s;
struct fimc_is_video video_m1s;
struct fimc_is_video video_m0p;
struct fimc_is_video video_m1p;
struct fimc_is_video video_m2p;
struct fimc_is_video video_m3p;
struct fimc_is_video video_m4p;
struct fimc_is_video video_vra;
/* spi */
struct fimc_is_spi spi0;
struct fimc_is_spi spi1;
struct i2c_client *client0;
struct i2c_client *client1;
struct i2c_client *client2;
struct fimc_is_vender vender;
};
#if defined(CONFIG_VIDEOBUF2_CMA_PHYS)
extern const struct fimc_is_vb2 fimc_is_vb2_cma;
#elif defined(CONFIG_VIDEOBUF2_ION)
extern const struct fimc_is_vb2 fimc_is_vb2_ion;
#endif
void fimc_is_mem_suspend(void *alloc_ctxes);
void fimc_is_mem_resume(void *alloc_ctxes);
void fimc_is_mem_cache_clean(const void *start_addr, unsigned long size);
void fimc_is_mem_cache_inv(const void *start_addr, unsigned long size);
int fimc_is_init_set(struct fimc_is_core *dev , u32 val);
int fimc_is_load_fw(struct fimc_is_core *dev);
int fimc_is_load_setfile(struct fimc_is_core *dev);
int fimc_is_otf_close(struct fimc_is_device_ischain *ischain);
#define CALL_POPS(s, op, args...) (((s) && (s)->pdata && (s)->pdata->op) ? ((s)->pdata->op(&(s)->pdev->dev)) : -EPERM)
#endif /* FIMC_IS_CORE_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,157 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DEBUG_H
#define FIMC_IS_DEBUG_H
#include "fimc-is-video.h"
#define DEBUG_SENTENCE_MAX 300
#ifdef DBG_DRAW_DIGIT
#define DBG_DIGIT_CNT 20 /* Max count of total digit */
#define DBG_DIGIT_W 16
#define DBG_DIGIT_H 32
#define DBG_DIGIT_MARGIN_W_INIT 128
#define DBG_DIGIT_MARGIN_H_INIT 64
#define DBG_DIGIT_MARGIN_W 8
#define DBG_DIGIT_MARGIN_H 2
#define DBG_DIGIT_TAG(row, col, queue, frame, digit) \
do { \
ulong addr; \
u32 width, height, pixelformat, bitwidth; \
addr = queue->buf_kva[frame->index][0]; \
width = (frame->width) ? frame->width : queue->framecfg.width; \
height = (frame->height) ? frame->height : queue->framecfg.height; \
pixelformat = queue->framecfg.format->pixelformat; \
bitwidth = queue->framecfg.format->bitwidth; \
fimc_is_draw_digit(addr, width, height, pixelformat, bitwidth, \
row, col, digit); \
} while(0)
#else
#define DBG_DIGIT_TAG(row, col, queue, frame, digit)
#endif
#define MAX_EVENT_LOG_NUM 32
#define FIMC_IS_EVENT_MAX_NUM SZ_4K
#define FIMC_IS_EVENT_PRINT_MAX 512
enum fimc_is_debug_state {
FIMC_IS_DEBUG_OPEN
};
enum dbg_dma_dump_type {
DBG_DMA_DUMP_IMAGE,
DBG_DMA_DUMP_META,
};
typedef enum fimc_is_event_type {
/* INTERUPPT */
FIMC_IS_EVENT_INT_START = 0x0,
FIMC_IS_EVENT_INT_END,
/* SHOT */
FIMC_IS_EVENT_SHOT_START = 0x10,
FIMC_IS_EVENT_SHOT_DONE,
/* DDK MEM ALLOC */
FIMC_IS_EVENT_LIB_MEM_ALLOC = 0x300,
FIMC_IS_EVENT_LIB_MEM_FREE,
FIMC_IS_EVENT_SIZE = 0x1000,
FIMC_IS_EVENT_MAX,
} fimc_is_event_type_t;
struct fimc_is_debug_event_size {
unsigned int id;
unsigned int total_width;
unsigned int total_height;
unsigned int width;
unsigned int height;
unsigned int position_x;
unsigned int position_y;
};
struct fimc_is_debug_event_msg {
char text[MAX_EVENT_LOG_NUM];
};
struct fimc_is_debug_event_lib_memory {
char comm[TASK_COMM_LEN];
u32 size;
ulong kvaddr;
dma_addr_t dvaddr;
};
struct fimc_is_debug_event {
unsigned int num;
ktime_t time;
fimc_is_event_type_t type;
union {
struct fimc_is_debug_event_size size;
struct fimc_is_debug_event_msg msg;
struct fimc_is_debug_event_lib_memory lib_mem;
} event_data;
};
struct fimc_is_debug {
struct dentry *root;
struct dentry *logfile;
struct dentry *imgfile;
struct dentry *event;
/* log dump */
size_t read_vptr;
struct fimc_is_minfo *minfo;
/* debug message */
size_t dsentence_pos;
char dsentence[DEBUG_SENTENCE_MAX];
unsigned long state;
/* event message */
struct fimc_is_debug_event event_log[FIMC_IS_EVENT_MAX_NUM];
atomic_t event_index;
/* test */
char build_date[MAX_EVENT_LOG_NUM];
unsigned int iknownothing;
};
extern struct fimc_is_debug fimc_is_debug;
int fimc_is_debug_probe(void);
int fimc_is_debug_open(struct fimc_is_minfo *minfo);
int fimc_is_debug_close(void);
void fimc_is_dmsg_init(void);
void fimc_is_dmsg_concate(const char *fmt, ...);
char *fimc_is_dmsg_print(void);
void fimc_is_print_buffer(char *buffer, size_t len);
int fimc_is_debug_dma_dump(struct fimc_is_queue *queue, u32 index, u32 vid, u32 type);
#ifdef DBG_DRAW_DIGIT
void fimc_is_draw_digit(ulong addr, int width, int height, u32 pixelformat,
u32 bitwidth, int row_index, int col_index, u64 digit);
#endif
int imgdump_request(ulong cookie, ulong kvaddr, size_t size);
#ifdef ENABLE_DBG_EVENT
void fimc_is_debug_event_add(struct fimc_is_debug *info,
struct fimc_is_debug_event *event);
int fimc_is_debug_info_dump(struct seq_file *s, struct fimc_is_debug *info);
void fimc_is_event_test(struct fimc_is_debug *info);
void fimc_is_event_add_lib_mem( bool type, char* comm, u32 size, ulong kvaddr,
dma_addr_t dvaddr);
#endif
#endif

View file

@ -0,0 +1,616 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is/mipi-csi functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
#include "fimc-is-config.h"
#include "fimc-is-regs.h"
#include "fimc-is-hw.h"
#include "fimc-is-device-csi.h"
#include "fimc-is-device-sensor.h"
#include "fimc-is-device-ischain.h"
#ifndef ENABLE_IS_CORE
#include "fimc-is-hw-csi.h"
#endif
static u32 get_hsync_settle(struct fimc_is_sensor_cfg *cfg,
const u32 cfgs, u32 width, u32 height, u32 framerate)
{
u32 settle;
u32 max_settle;
u32 proximity_framerate, proximity_settle;
u32 i;
settle = 0;
max_settle = 0;
proximity_framerate = 0;
proximity_settle = 0;
for (i = 0; i < cfgs; i++) {
if ((cfg[i].width == width) &&
(cfg[i].height == height) &&
(cfg[i].framerate == framerate)) {
settle = cfg[i].settle;
break;
}
if ((cfg[i].width == width) &&
(cfg[i].height == height) &&
(cfg[i].framerate > proximity_framerate)) {
proximity_settle = cfg[i].settle;
proximity_framerate = cfg[i].framerate;
}
if (cfg[i].settle > max_settle)
max_settle = cfg[i].settle;
}
if (!settle) {
if (proximity_settle) {
settle = proximity_settle;
} else {
/*
* return a max settle time value in above table
* as a default depending on the channel
*/
settle = max_settle;
warn("could not find proper settle time: %dx%d@%dfps",
width, height, framerate);
}
}
return settle;
}
static u32 get_vci_channel(struct fimc_is_vci *vci,
const u32 vcis, u32 pixelformat)
{
u32 i;
u32 index = vcis;
BUG_ON(!vci);
for (i = 0; i < vcis; i++) {
if (vci[i].pixelformat == pixelformat) {
index = i;
break;
}
}
if (index == vcis) {
err("invalid vc setting(foramt : %d)", pixelformat);
BUG();
}
return index;
}
#ifdef ENABLE_CSIISR
static irqreturn_t fimc_is_csi_isr(int irq, void *data)
{
u32 status;
struct fimc_is_device_csi *csi;
csi = data;
#ifndef ENABLE_IS_CORE
/* Below function get and clear interrupts */
status = csi_hw_g_irq_src(csi->base_reg, NULL, true);
if (status & S5PCSIS_INTSRC_FRAME_END) {
u32 vsync_cnt = 0;
/* Expecting vvalid is 1 in case of NO_ERROR */
if (likely(atomic_read(&csi->vvalid) == 1)) {
atomic_set(&csi->vvalid, 0);
} else if (atomic_read(&csi->vvalid) > 1) {
/* How many times occured FRAME START without END */
err("CSI%d : Frame start without Frame end %d times\n",
csi->instance, atomic_read(&csi->vvalid));
} else {
/* When vvalid is less than or equal ZERO */
pr_debug("CSI%d : Frame end without Frame start\n", csi->instance);
atomic_dec(&csi->vvalid);
}
atomic_set(&csi->vblank_count, atomic_read(&csi->vsync_count));
vsync_cnt = atomic_read(&csi->fcount);
v4l2_subdev_notify(*csi->subdev, CSI_NOTIFY_VBLANK, &vsync_cnt);
}
if (status & S5PCSIS_INTSRC_FRAME_START) {
u32 vsync_cnt = 0;
/* Expecting vvalid is 0 in case of NO_ERROR */
if (likely(atomic_read(&csi->vvalid) == 0)) {
atomic_set(&csi->vvalid, 1);
} else if (atomic_read(&csi->vvalid) < 0) {
/* How many times occured FRAME END without START */
err("CSI%d : Frame end without Frame start %ld times\n",
csi->instance, abs(atomic_read(&csi->vvalid)));
} else {
/* When vvalid is bigger than ZERO */
pr_debug("CSI%d : Frame start without Frame end\n", csi->instance);
atomic_inc(&csi->vvalid);
}
atomic_inc(&csi->fcount);
vsync_cnt = atomic_read(&csi->fcount);
v4l2_subdev_notify(*csi->subdev, CSI_NOTIFY_VSYNC, &vsync_cnt);
}
if (unlikely(status & S5PCSIS_INTSRC_ERRORS))
goto err_status;
return IRQ_HANDLED;
err_status:
/* TODO: Belows are Error interrupt handling. */
if (status & S5PCSIS_INTSRC_ERR_ID) {
err("CSI%d : ERR_ID, irq%d(%#X)\n",csi->instance, irq, status);
}
if (status & S5PCSIS_INTSRC_ERR_CRC) {
err("CSI%d : ERR_CRC, irq%d(%#X)\n",csi->instance, irq, status);
}
if (status & S5PCSIS_INTSRC_ERR_ECC) {
err("CSI%d : ERR_ECC, irq%d(%#X)\n",csi->instance, irq, status);
}
if (status & S5PCSIS_INTSRC_ERR_OVER) {
err("CSI%d : ERR_OVER, irq%d(%#X)\n",csi->instance, irq, status);
}
if (status & S5PCSIS_INTSRC_ERR_LOST_FE) {
err("CSI%d : ERR_LOST_FE, irq%d(%#X)\n",csi->instance, irq, status);
}
if (status & S5PCSIS_INTSRC_ERR_LOST_FS) {
err("CSI%d : ERR_LOST_FS, irq%d(%#X)\n",csi->instance, irq, status);
}
if (status & S5PCSIS_INTSRC_ERR_SOT_HS) {
err("CSI%d : ERR_SOT_HS, irq%d(%#X)\n",csi->instance, irq, status);
}
return IRQ_HANDLED;
#else
status = csi_hw_g_irq_src(csi->base_reg, NULL, true);
info("CSI%d : irq%d(%X)\n",csi->instance, irq, status);
return IRQ_HANDLED;
#endif
}
#endif
int fimc_is_csi_open(struct v4l2_subdev *subdev,
struct fimc_is_framemgr *framemgr)
{
int ret = 0;
u32 instance = 0;
struct fimc_is_device_csi *csi;
struct fimc_is_device_sensor *device;
BUG_ON(!subdev);
csi = v4l2_get_subdevdata(subdev);
if (!csi) {
err("csi is NULL");
ret = -EINVAL;
goto p_err;
}
device = v4l2_get_subdev_hostdata(subdev);
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
csi->sensor_cfgs = 0;
csi->sensor_cfg = NULL;
memset(&csi->image, 0, sizeof(struct fimc_is_image));
ret = request_irq(csi->irq,
fimc_is_csi_isr,
IRQF_SHARED,
"mipi-csi",
csi);
if (ret) {
err("request_irq(IRQ_MIPICSI %d) is fail(%d)", csi->irq, ret);
goto p_err;
}
#ifndef ENABLE_IS_CORE
atomic_set(&csi->vblank_count, 0);
atomic_set(&csi->vvalid, 0);
#endif
p_err:
return ret;
}
int fimc_is_csi_close(struct v4l2_subdev *subdev)
{
int ret = 0;
struct fimc_is_device_csi *csi;
BUG_ON(!subdev);
csi = v4l2_get_subdevdata(subdev);
if (!csi) {
err("csi is NULL");
ret = -EINVAL;
goto p_err;
}
#ifdef ENABLE_CSIISR
free_irq(csi->irq, csi);
#endif
p_err:
return ret;
}
/* value : module enum */
static int csi_init(struct v4l2_subdev *subdev, u32 value)
{
int ret = 0;
struct fimc_is_device_csi *csi;
struct fimc_is_module_enum *module;
struct fimc_is_device_sensor *device;
BUG_ON(!subdev);
csi = v4l2_get_subdevdata(subdev);
if (!csi) {
err("csi is NULL");
ret = -EINVAL;
goto p_err;
}
device = container_of(csi->subdev, struct fimc_is_device_sensor, subdev_csi);
module = &device->module_enum[value];
csi->sensor_cfgs = module->cfgs;
csi->sensor_cfg = module->cfg;
csi->vcis = module->vcis;
csi->vci = module->vci;
csi->image.framerate = SENSOR_DEFAULT_FRAMERATE; /* default frame rate */
csi->mode = module->mode;
csi->lanes = module->lanes;
csi->image.format.bitwidth = module->bitwidth;
p_err:
return ret;
}
static int csi_s_power(struct v4l2_subdev *subdev,
int on)
{
int ret = 0;
struct fimc_is_device_csi *csi;
BUG_ON(!subdev);
csi = (struct fimc_is_device_csi *)v4l2_get_subdevdata(subdev);
if (!csi) {
err("csi is NULL");
return -EINVAL;
}
ret = exynos5_csis_phy_enable(csi->instance, on);
if (ret) {
err("fail to csi%d power on", csi->instance);
goto p_err;
}
p_err:
mdbgd_front("%s(%d, %d)\n", csi, __func__, on, ret);
return ret;
}
static const struct v4l2_subdev_core_ops core_ops = {
.init = csi_init,
.s_power = csi_s_power
};
static int csi_stream_on(struct fimc_is_device_csi *csi)
{
int ret = 0;
u32 settle, index;
u32 __iomem *base_reg;
struct fimc_is_device_sensor *device;
BUG_ON(!csi);
BUG_ON(!csi->sensor_cfg);
device = container_of(csi->subdev, struct fimc_is_device_sensor, subdev_csi);
base_reg = csi->base_reg;
if (!device->cfg) {
merr("[CSI] cfg is NULL", csi);
ret = -EINVAL;
goto p_err;
}
csi->lanes = device->cfg->lanes;
settle = get_hsync_settle(
csi->sensor_cfg,
csi->sensor_cfgs,
csi->image.window.width,
csi->image.window.height,
csi->image.framerate);
minfo("[CSI] settle(%dx%d@%d) = %d\n", csi,
csi->image.window.width,
csi->image.window.height,
csi->image.framerate,
settle);
index = get_vci_channel(csi->vci, csi->vcis, csi->image.format.pixelformat);
minfo("[CSI] vci(0x%X) = %d\n", csi, csi->image.format.pixelformat, index);
csi_hw_reset(base_reg);
csi_hw_s_settle(base_reg, settle);
csi_hw_s_lane(base_reg, &csi->image, csi->lanes);
csi_hw_s_control(base_reg, CSIS_CTRL_INTERLEAVE_MODE, csi->mode);
if (csi->mode == CSI_MODE_CH0_ONLY) {
csi_hw_s_config(base_reg,
CSI_VIRTUAL_CH_0,
&csi->vci[index].config[CSI_VIRTUAL_CH_0],
csi->image.window.width,
csi->image.window.height);
} else {
csi_hw_s_config(base_reg,
CSI_VIRTUAL_CH_0,
&csi->vci[index].config[CSI_VIRTUAL_CH_0],
csi->image.window.width,
csi->image.window.height);
csi_hw_s_config(base_reg,
CSI_VIRTUAL_CH_1,
&csi->vci[index].config[CSI_VIRTUAL_CH_1],
csi->image.window.width,
csi->image.window.height);
csi_hw_s_config(base_reg,
CSI_VIRTUAL_CH_2,
&csi->vci[index].config[CSI_VIRTUAL_CH_2],
csi->image.window.width,
csi->image.window.height);
}
csi_hw_s_irq_msk(base_reg, true);
csi_hw_enable(base_reg);
#ifdef DBG_DUMPREG
csi_hw_dump(base_reg);
#endif
p_err:
return ret;
}
static int csi_stream_off(struct fimc_is_device_csi *csi)
{
int ret = 0;
u32 __iomem *base_reg;
BUG_ON(!csi);
base_reg = csi->base_reg;
csi_hw_s_irq_msk(base_reg, false);
csi_hw_disable(base_reg);
return ret;
}
static int csi_s_stream(struct v4l2_subdev *subdev, int enable)
{
int ret = 0;
struct fimc_is_device_csi *csi;
BUG_ON(!subdev);
csi = (struct fimc_is_device_csi *)v4l2_get_subdevdata(subdev);
if (!csi) {
err("csi is NULL");
return -EINVAL;
}
if (enable) {
ret = csi_stream_on(csi);
if (ret) {
err("csi_stream_on is fail(%d)", ret);
goto p_err;
}
} else {
ret = csi_stream_off(csi);
if (ret) {
err("csi_stream_off is fail(%d)", ret);
goto p_err;
}
#ifndef ENABLE_IS_CORE
atomic_set(&csi->vvalid, 0);
#endif
}
p_err:
return 0;
}
static int csi_s_param(struct v4l2_subdev *subdev, struct v4l2_streamparm *param)
{
int ret = 0;
struct fimc_is_device_csi *csi;
struct v4l2_captureparm *cp;
struct v4l2_fract *tpf;
BUG_ON(!subdev);
BUG_ON(!param);
cp = &param->parm.capture;
tpf = &cp->timeperframe;
csi = (struct fimc_is_device_csi *)v4l2_get_subdevdata(subdev);
if (!csi) {
err("csi is NULL");
return -EINVAL;
}
csi->image.framerate = tpf->denominator / tpf->numerator;
mdbgd_front("%s(%d, %d)\n", csi, __func__, csi->image.framerate, ret);
return ret;
}
static int csi_s_format(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *fmt)
{
int ret = 0;
struct fimc_is_device_csi *csi;
BUG_ON(!subdev);
BUG_ON(!fmt);
csi = (struct fimc_is_device_csi *)v4l2_get_subdevdata(subdev);
if (!csi) {
err("csi is NULL");
return -EINVAL;
}
csi->image.window.offs_h = 0;
csi->image.window.offs_v = 0;
csi->image.window.width = fmt->width;
csi->image.window.height = fmt->height;
csi->image.window.o_width = fmt->width;
csi->image.window.o_height = fmt->height;
csi->image.format.pixelformat = fmt->code;
csi->image.format.field = fmt->field;
mdbgd_front("%s(%dx%d, %X)\n", csi, __func__, fmt->width, fmt->height, fmt->code);
return ret;
}
static int csi_s_buffer(struct v4l2_subdev *subdev, void *buf, unsigned int *size)
{
return 0;
}
static const struct v4l2_subdev_video_ops video_ops = {
.s_stream = csi_s_stream,
.s_parm = csi_s_param,
.s_mbus_fmt = csi_s_format,
.s_rx_buffer = csi_s_buffer
};
static const struct v4l2_subdev_ops subdev_ops = {
.core = &core_ops,
.video = &video_ops
};
int fimc_is_csi_probe(void *parent, u32 instance)
{
int ret = 0;
struct v4l2_subdev *subdev_csi;
struct fimc_is_device_csi *csi;
struct fimc_is_device_sensor *device = parent;
struct resource *mem_res;
struct platform_device *pdev;
BUG_ON(!device);
subdev_csi = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (!subdev_csi) {
merr("subdev_csi is NULL", device);
ret = -ENOMEM;
goto p_err;
}
device->subdev_csi = subdev_csi;
csi = kzalloc(sizeof(struct fimc_is_device_csi), GFP_KERNEL);
if (!csi) {
merr("csi is NULL", device);
ret = -ENOMEM;
goto p_err_free1;
}
/* Get SFR base register */
pdev = device->pdev;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
probe_err("Failed to get io memory region(%p)", mem_res);
ret = -EBUSY;
goto err_get_resource;
}
csi->regs_start = mem_res->start;
csi->regs_end = mem_res->end;
csi->base_reg = devm_ioremap_nocache(&pdev->dev, mem_res->start, resource_size(mem_res));
if (!csi->base_reg) {
probe_err("Failed to remap io region(%p)", csi->base_reg);
ret = -ENOMEM;
goto err_get_resource;
}
/* Get IRQ number */
csi->irq = platform_get_irq(pdev, 0);
if (csi->irq < 0) {
probe_err("Failed to get csi_irq(%d)", csi->irq);
ret = -EBUSY;
goto err_get_irq;
}
/* pointer to me from device sensor */
csi->subdev = &device->subdev_csi;
csi->instance = instance;
v4l2_subdev_init(subdev_csi, &subdev_ops);
v4l2_set_subdevdata(subdev_csi, csi);
v4l2_set_subdev_hostdata(subdev_csi, device);
snprintf(subdev_csi->name, V4L2_SUBDEV_NAME_SIZE, "csi-subdev.%d", instance);
ret = v4l2_device_register_subdev(&device->v4l2_dev, subdev_csi);
if (ret) {
merr("v4l2_device_register_subdev is fail(%d)", device, ret);
goto err_reg_v4l2_subdev;
}
info("[%d][FRT:D] %s(%d)\n", instance, __func__, ret);
return 0;
err_reg_v4l2_subdev:
err_get_irq:
iounmap(csi->base_reg);
err_get_resource:
kfree(csi);
p_err_free1:
kfree(subdev_csi);
device->subdev_csi = NULL;
p_err:
err("[%d][FRT:D] %s(%d)\n", instance, __func__, ret);
return ret;
}

View file

@ -0,0 +1,95 @@
#ifndef FIMC_IS_DEVICE_CSI_H
#define FIMC_IS_DEVICE_CSI_H
#include <linux/interrupt.h>
#include <media/v4l2-device.h>
#include "fimc-is-hw.h"
#include "fimc-is-type.h"
#include "fimc-is-framemgr.h"
#include "fimc-is-subdev-ctrl.h"
#ifndef ENABLE_IS_CORE
#define CSI_NOTIFY_VSYNC 10
#define CSI_NOTIFY_VBLANK 11
#endif
#define CSI_LINE_RATIO 14 /* 70% */
enum fimc_is_csi_state {
/* flite join ischain */
CSIS_JOIN_ISCHAIN,
/* one the fly output */
CSIS_OTF_WITH_3AA,
/* If it's dummy, H/W setting can't be applied */
CSIS_DUMMY,
/* WDMA flag */
CSIS_DMA_ENABLE,
CSIS_START_STREAM,
/* runtime buffer done state for error */
CSIS_BUF_ERR_VC0,
CSIS_BUF_ERR_VC1,
CSIS_BUF_ERR_VC2,
CSIS_BUF_ERR_VC3,
};
struct fimc_is_device_csi {
/* channel information */
u32 instance;
u32 __iomem *base_reg;
resource_size_t regs_start;
resource_size_t regs_end;
int irq;
/* for settle time */
u32 sensor_cfgs;
struct fimc_is_sensor_cfg *sensor_cfg;
/* for vci setting */
u32 vcis;
struct fimc_is_vci *vci;
/* image configuration */
u32 mode;
u32 lanes;
u32 mipi_speed;
struct fimc_is_image image;
unsigned long state;
/* for DMA feature */
struct fimc_is_framemgr *framemgr;
u32 overflow_cnt;
u32 sw_checker;
atomic_t fcount;
struct tasklet_struct tasklet_csis_str;
struct tasklet_struct tasklet_csis_end;
struct tasklet_struct tasklet_csis_line;
struct tasklet_struct tasklet_csis_dma[CSI_VIRTUAL_CH_MAX];
int pre_dma_enable[CSI_VIRTUAL_CH_MAX];
/* subdev slots for dma */
struct fimc_is_subdev *dma_subdev[CSI_VIRTUAL_CH_MAX];
/* pointer address from device sensor */
struct v4l2_subdev **subdev;
struct phy *phy;
u32 error_id;
#ifndef ENABLE_IS_CORE
atomic_t vblank_count; /* Increase at CSI frame end */
atomic_t vvalid; /* set 1 while vvalid period */
#endif
};
int __must_check fimc_is_csi_probe(void *parent, u32 instance);
int __must_check fimc_is_csi_open(struct v4l2_subdev *subdev, struct fimc_is_framemgr *framemgr);
int __must_check fimc_is_csi_close(struct v4l2_subdev *subdev);
/* for DMA feature */
extern u32 __iomem *notify_fcount_sen0;
extern u32 __iomem *notify_fcount_sen1;
extern u32 __iomem *notify_fcount_sen2;
extern u32 __iomem *last_fcount0;
extern u32 __iomem *last_fcount1;
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,123 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DEVICE_FLITE_H
#define FIMC_IS_DEVICE_FLITE_H
#include <linux/interrupt.h>
#include "fimc-is-type.h"
#define EXPECT_FRAME_START 0
#define EXPECT_FRAME_END 1
#define FLITE_NOTIFY_FSTART 0
#define FLITE_NOTIFY_FEND 1
#define FLITE_ENABLE_FLAG 1
#define FLITE_ENABLE_MASK 0xFFFF
#define FLITE_ENABLE_SHIFT 0
#define FLITE_NOWAIT_FLAG 1
#define FLITE_NOWAIT_MASK 0xFFFF0000
#define FLITE_NOWAIT_SHIFT 16
#define FLITE_OVERFLOW_COUNT 10
#define FLITE_VVALID_TIME_BASE 32 /* ms */
struct fimc_is_device_sensor;
enum fimc_is_flite_state {
/* finish state */
FLITE_LAST_CAPTURE = 0,
/* flite join ischain */
FLITE_JOIN_ISCHAIN,
/* one the fly output */
FLITE_OTF_WITH_3AA,
/* If it's dummy, H/W setting can't be applied */
FLITE_DUMMY,
/* WDMA flag */
FLITE_DMA_ENABLE,
FLITE_START_STREAM
};
enum fimc_is_flite_buf_done_mode {
FLITE_BUF_DONE_NORMAL = 0, /* when end-irq */
FLITE_BUF_DONE_EARLY = 1, /* when delayed work queue since start-irq */
};
/*
* 10p means 10% early than end irq. We supposed that VVALID time is variable
* ex. 32 * 0.1 = 3ms, early interval is (33 - 3) = 29ms
* 32 * 0.2 = 6ms, (33 - 6) = 26ms
* 32 * 0.3 = 9ms, (33 - 9) = 23ms
* 32 * 0.4 = 12ms, (33 - 12) = 20ms
*/
enum fimc_is_flite_early_buf_done_mode {
FLITE_BUF_EARLY_NOTHING = 0,
FLITE_BUF_EARLY_10P = 1, /* 10%(29ms) 3ms */
FLITE_BUF_EARLY_20P = 2, /* 20%(26ms) 6ms */
FLITE_BUF_EARLY_30P = 3, /* 30%(23ms) 9ms */
FLITE_BUF_EARLY_40P = 4, /* 40%(20ms) 12ms */
};
struct fimc_is_device_flite {
u32 instance;
u32 __iomem *base_reg;
resource_size_t regs_start;
resource_size_t regs_end;
int irq;
unsigned long state;
wait_queue_head_t wait_queue;
struct fimc_is_image image;
struct fimc_is_framemgr *framemgr;
u32 overflow_cnt;
u32 csi; /* which csi channel is connceted */
u32 group; /* which 3aa gorup is connected when otf is enable */
u32 sw_checker;
atomic_t fcount;
u32 tasklet_param_str;
struct tasklet_struct tasklet_flite_str;
u32 tasklet_param_end;
struct tasklet_struct tasklet_flite_end;
/* for early buffer done */
u32 buf_done_mode;
u32 early_buf_done_mode;
u32 buf_done_wait_time;
bool early_work_skip;
bool early_work_called;
struct tasklet_struct tasklet_flite_early_end;
struct workqueue_struct *early_workqueue;
struct delayed_work early_work_wq;
void (*chk_early_buf_done)(struct fimc_is_device_flite *flite, u32 framerate, u32 position);
/* pointer address from device sensor */
struct v4l2_subdev **subdev;
};
int fimc_is_flite_probe(struct fimc_is_device_sensor *device,
u32 instance);
int fimc_is_flite_open(struct v4l2_subdev *subdev,
struct fimc_is_framemgr *framemgr);
int fimc_is_flite_close(struct v4l2_subdev *subdev);
extern u32 __iomem *notify_fcount_sen0;
extern u32 __iomem *notify_fcount_sen1;
extern u32 __iomem *notify_fcount_sen2;
extern u32 __iomem *notify_fcount_sen3;
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,323 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DEVICE_ISCHAIN_H
#define FIMC_IS_DEVICE_ISCHAIN_H
#include <linux/pm_qos.h>
#include "fimc-is-mem.h"
#include "fimc-is-subdev-ctrl.h"
#ifndef ENABLE_IS_CORE
#include "hardware/fimc-is-hw-control.h"
#endif
#include "fimc-is-groupmgr.h"
#include "fimc-is-resourcemgr.h"
#include "fimc-is-pipe.h"
#include "fimc-is-binary.h"
#define SENSOR_MAX_CTL 0x3
#define SENSOR_MAX_CTL_MASK (SENSOR_MAX_CTL-1)
#define REPROCESSING_FLAG 0x80000000
#define REPROCESSING_MASK 0xF0000000
#define REPROCESSING_SHIFT 28
#define OTF_3AA_MASK 0x0F000000
#define OTF_3AA_SHIFT 24
#define SSX_VINDEX_MASK 0x00FF0000
#define SSX_VINDEX_SHIFT 16
#define TAX_VINDEX_MASK 0x0000FF00
#define TAX_VINDEX_SHIFT 8
#define MODULE_MASK 0x000000FF
#define FIMC_IS_SETFILE_MASK 0x0000FFFF
#define FIMC_IS_SCENARIO_MASK 0xFFFF0000
#define FIMC_IS_SCENARIO_SHIFT 16
#define FIMC_IS_ISP_CRANGE_MASK 0x0F000000
#define FIMC_IS_ISP_CRANGE_SHIFT 24
#define FIMC_IS_SCC_CRANGE_MASK 0x00F00000
#define FIMC_IS_SCC_CRANGE_SHIFT 20
#define FIMC_IS_SCP_CRANGE_MASK 0x000F0000
#define FIMC_IS_SCP_CRANGE_SHIFT 16
#define FIMC_IS_CRANGE_FULL 0
#define FIMC_IS_CRANGE_LIMITED 1
/*global state*/
enum fimc_is_ischain_state {
FIMC_IS_ISCHAIN_OPENING,
FIMC_IS_ISCHAIN_CLOSING,
FIMC_IS_ISCHAIN_OPEN,
FIMC_IS_ISCHAIN_INITING,
FIMC_IS_ISCHAIN_INIT,
FIMC_IS_ISCHAIN_START,
FIMC_IS_ISCHAIN_LOADED,
FIMC_IS_ISCHAIN_POWER_ON,
FIMC_IS_ISCHAIN_OPEN_STREAM,
FIMC_IS_ISCHAIN_REPROCESSING,
};
enum fimc_is_camera_device {
CAMERA_SINGLE_REAR,
CAMERA_SINGLE_FRONT,
};
struct fast_control_mgr {
u32 fast_capture_count;
};
struct fimc_is_device_ischain {
struct platform_device *pdev;
struct exynos_platform_fimc_is *pdata;
struct fimc_is_resourcemgr *resourcemgr;
struct fimc_is_groupmgr *groupmgr;
struct fimc_is_devicemgr *devicemgr;
struct fimc_is_interface *interface;
#ifndef ENABLE_IS_CORE
struct fimc_is_hardware *hardware;
#endif
struct fimc_is_mem *mem;
u32 instance;
u32 instance_sensor;
u32 module;
struct fimc_is_minfo *minfo;
struct fimc_is_path_info path;
struct is_region *is_region;
ulong kvaddr_shared;
dma_addr_t dvaddr_shared;
unsigned long state;
atomic_t group_open_cnt;
atomic_t open_cnt;
atomic_t init_cnt;
u32 setfile;
struct camera2_uctl cur_peri_ctl;
struct camera2_uctl peri_ctls[SENSOR_MAX_CTL];
/* isp margin */
u32 margin_left;
u32 margin_right;
u32 margin_width;
u32 margin_top;
u32 margin_bottom;
u32 margin_height;
struct fimc_is_pipe pipe;
struct fimc_is_group group_3aa;
struct fimc_is_subdev txc;
struct fimc_is_subdev txp;
struct fimc_is_group group_isp;
struct fimc_is_subdev ixc;
struct fimc_is_subdev ixp;
struct fimc_is_subdev drc;
struct fimc_is_subdev scc;
struct fimc_is_group group_dis;
struct fimc_is_subdev odc;
struct fimc_is_subdev dnr;
struct fimc_is_subdev scp;
struct fimc_is_group group_mcs;
struct fimc_is_subdev m0p;
struct fimc_is_subdev m1p;
struct fimc_is_subdev m2p;
struct fimc_is_subdev m3p;
struct fimc_is_subdev m4p;
struct fimc_is_group group_vra;
#ifdef ENABLE_FD_SW
struct fimc_is_lib *fd_lib;
#endif
u32 private_data;
struct fimc_is_device_sensor *sensor;
struct pm_qos_request user_qos;
/* Async metadata control to reduce frame delay */
struct fast_control_mgr fastctlmgr;
};
/*global function*/
int fimc_is_ischain_probe(struct fimc_is_device_ischain *device,
struct fimc_is_interface *interface,
struct fimc_is_resourcemgr *resourcemgr,
struct fimc_is_groupmgr *groupmgr,
struct fimc_is_devicemgr *devicemgr,
struct fimc_is_mem *mem,
struct platform_device *pdev,
u32 instance);
int fimc_is_ischain_g_capability(struct fimc_is_device_ischain *this,
ulong user_ptr);
void fimc_is_ischain_meta_invalid(struct fimc_is_frame *frame);
int fimc_is_ischain_open_wrap(struct fimc_is_device_ischain *device, bool EOS);
int fimc_is_ischain_close_wrap(struct fimc_is_device_ischain *device);
int fimc_is_ischain_start_wrap(struct fimc_is_device_ischain *device,
struct fimc_is_group *group);
int fimc_is_ischain_stop_wrap(struct fimc_is_device_ischain *device,
struct fimc_is_group *group);
void fimc_is_ischain_version(enum fimc_is_bin_type type, const char *load_bin, u32 size);
/* 3AA subdev */
int fimc_is_ischain_3aa_open(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_3aa_close(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_3aa_s_input(struct fimc_is_device_ischain *device,
u32 stream_type,
u32 module_id,
u32 video_id,
u32 input_type,
u32 stream_leader);
int fimc_is_ischain_3aa_buffer_queue(struct fimc_is_device_ischain *device,
struct fimc_is_queue *queue,
u32 index);
int fimc_is_ischain_3aa_buffer_finish(struct fimc_is_device_ischain *device,
u32 index);
/* isp subdev */
int fimc_is_ischain_isp_open(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_isp_close(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_isp_s_input(struct fimc_is_device_ischain *device,
u32 stream_type,
u32 module_id,
u32 video_id,
u32 input_type,
u32 stream_leader);
int fimc_is_ischain_isp_buffer_queue(struct fimc_is_device_ischain *device,
struct fimc_is_queue *queue,
u32 index);
int fimc_is_ischain_isp_buffer_finish(struct fimc_is_device_ischain *this,
u32 index);
/*scc subdev*/
/*scp subdev*/
int fimc_is_ischain_scp_s_format(struct fimc_is_device_ischain *device,
u32 pixelformat, u32 width, u32 height);
/* dis subdev */
int fimc_is_ischain_dis_open(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_dis_close(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_dis_s_input(struct fimc_is_device_ischain *device,
u32 stream_type,
u32 module_id,
u32 video_id,
u32 input_type,
u32 stream_leader);
int fimc_is_ischain_dis_buffer_queue(struct fimc_is_device_ischain *device,
struct fimc_is_queue *queue,
u32 index);
int fimc_is_ischain_dis_buffer_finish(struct fimc_is_device_ischain *this,
u32 index);
/* MCSC subdev */
int fimc_is_ischain_mcs_open(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_mcs_close(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_mcs_s_input(struct fimc_is_device_ischain *device,
u32 stream_type,
u32 module_id,
u32 video_id,
u32 otf_input,
u32 stream_leader);
int fimc_is_ischain_mcs_buffer_queue(struct fimc_is_device_ischain *device,
struct fimc_is_queue *queue,
u32 index);
int fimc_is_ischain_mcs_buffer_finish(struct fimc_is_device_ischain *device,
u32 index);
/* vra subdev */
int fimc_is_ischain_vra_open(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_vra_close(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_vra_s_input(struct fimc_is_device_ischain *device,
u32 stream_type,
u32 module_id,
u32 video_id,
u32 otf_input,
u32 stream_leader);
int fimc_is_ischain_vra_buffer_queue(struct fimc_is_device_ischain *device,
struct fimc_is_queue *queue,
u32 index);
int fimc_is_ischain_vra_buffer_finish(struct fimc_is_device_ischain *this,
u32 index);
/*special api for sensor*/
int fimc_is_ischain_camctl(struct fimc_is_device_ischain *this,
struct fimc_is_frame *frame,
u32 fcount);
int fimc_is_ischain_tag(struct fimc_is_device_ischain *ischain,
struct fimc_is_frame *frame);
int fimc_is_itf_stream_on(struct fimc_is_device_ischain *this);
int fimc_is_itf_stream_off(struct fimc_is_device_ischain *this);
int fimc_is_itf_process_start(struct fimc_is_device_ischain *device,
u32 group);
int fimc_is_itf_process_stop(struct fimc_is_device_ischain *device,
u32 group);
int fimc_is_itf_force_stop(struct fimc_is_device_ischain *device,
u32 group);
int fimc_is_itf_map(struct fimc_is_device_ischain *device,
u32 group, u32 shot_addr, u32 shot_size);
int fimc_is_itf_grp_shot(struct fimc_is_device_ischain *device,
struct fimc_is_group *group,
struct fimc_is_frame *frame);
int fimc_is_itf_i2c_lock(struct fimc_is_device_ischain *this,
int i2c_clk, bool lock);
int fimc_is_itf_s_param(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame,
u32 lindex,
u32 hindex,
u32 indexes);
void * fimc_is_itf_g_param(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame,
u32 index);
void fimc_is_itf_storefirm(struct fimc_is_device_ischain *device);
void fimc_is_itf_restorefirm(struct fimc_is_device_ischain *device);
int fimc_is_itf_set_fwboot(struct fimc_is_device_ischain *device, u32 val);
int fimc_is_ischain_buf_tag(struct fimc_is_device_ischain *device,
struct fimc_is_subdev *subdev,
struct fimc_is_frame *ldr_frame,
u32 pixelformat,
u32 width,
u32 height,
u32 target_addr[]);
extern const struct fimc_is_queue_ops fimc_is_ischain_3aa_ops;
extern const struct fimc_is_queue_ops fimc_is_ischain_isp_ops;
extern const struct fimc_is_queue_ops fimc_is_ischain_dis_ops;
extern const struct fimc_is_queue_ops fimc_is_ischain_mcs_ops;
extern const struct fimc_is_queue_ops fimc_is_ischain_vra_ops;
extern const struct fimc_is_queue_ops fimc_is_ischain_subdev_ops;
int fimc_is_itf_power_down(struct fimc_is_interface *interface);
int fimc_is_ischain_power(struct fimc_is_device_ischain *this, int on);
#define IS_EQUAL_COORD(i, o) \
(((i)[0] != (o)[0]) || ((i)[1] != (o)[1]) || \
((i)[2] != (o)[2]) || ((i)[3] != (o)[3]))
#define IS_NULL_COORD(c) \
(!(c)[0] && !(c)[1] && !(c)[2] && !(c)[3])
#endif

View file

@ -0,0 +1,900 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include <linux/i2c.h>
#include <exynos-fimc-is-preprocessor.h>
#include "fimc-is-video.h"
#include "fimc-is-dt.h"
#include "fimc-is-spi.h"
#include "fimc-is-device-preprocessor.h"
#include "fimc-is-core.h"
#include "fimc-is-dvfs.h"
#ifdef CONFIG_COMPANION_FACTORY_VALIDATION
#include "fimc-is-companion.h"
#endif
extern struct pm_qos_request exynos_isp_qos_int;
extern struct pm_qos_request exynos_isp_qos_mem;
extern struct pm_qos_request exynos_isp_qos_cam;
extern struct pm_qos_request exynos_isp_qos_disp;
int fimc_is_preproc_g_module(struct fimc_is_device_preproc *device,
struct fimc_is_module_enum **module)
{
int ret = 0;
BUG_ON(!device);
*module = device->module;
if (!*module) {
merr("module is NULL", device);
ret = -EINVAL;
goto p_err;
}
if (!(*module)->pdata) {
merr("module->pdata is NULL", device);
ret = -EINVAL;
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_preproc_mclk_on(struct fimc_is_device_preproc *device)
{
int ret = 0;
struct fimc_is_core *core;
struct exynos_platform_fimc_is_preproc *pdata;
struct fimc_is_module_enum *module;
BUG_ON(!device);
BUG_ON(!device->pdev);
BUG_ON(!device->pdata);
pdata = device->pdata;
if (test_bit(FIMC_IS_PREPROC_MCLK_ON, &device->state)) {
err("%s : already clk on", __func__);
goto p_err;
}
if (pdata->id >= FIMC_IS_SENSOR_COUNT) {
err("pdata id is inavalid(%d)", pdata->id);
ret = -EINVAL;
goto p_err;
}
core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev);
if (!core) {
err("core is NULL");
ret = -ENODEV;
goto p_err;
}
ret = fimc_is_preproc_g_module(device, &module);
if (ret) {
merr("fimc_is_sensor_g_module is fail(%d)", device, ret);
goto p_err;
}
/* preprocessor initialization need together with sensor init */
ret = fimc_is_sensor_mclk_on(&core->sensor[pdata->id], pdata->scenario, module->pdata->mclk_ch);
if (ret) {
err("fimc_is_sensor_mclk_on is fail(%d)", ret);
goto p_err;
}
if (!pdata->mclk_on) {
err("mclk_on is NULL");
ret = -EINVAL;
goto p_err;
}
ret = pdata->mclk_on(&device->pdev->dev, pdata->scenario, pdata->mclk_ch);
if (ret) {
err("mclk_on is fail(%d)", ret);
goto p_err;
}
set_bit(FIMC_IS_PREPROC_MCLK_ON, &device->state);
p_err:
return ret;
}
static int fimc_is_preproc_mclk_off(struct fimc_is_device_preproc *device)
{
int ret = 0;
struct fimc_is_core *core;
struct exynos_platform_fimc_is_preproc *pdata;
struct fimc_is_module_enum *module;
BUG_ON(!device);
BUG_ON(!device->pdev);
BUG_ON(!device->pdata);
pdata = device->pdata;
if (!test_bit(FIMC_IS_PREPROC_MCLK_ON, &device->state)) {
err("%s : already clk off", __func__);
goto p_err;
}
if (pdata->id >= FIMC_IS_SENSOR_COUNT) {
err("pdata id is inavalid(%d)", pdata->id);
ret = -EINVAL;
goto p_err;
}
core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev);
if (!core) {
err("core is NULL");
ret = -ENODEV;
goto p_err;
}
ret = fimc_is_preproc_g_module(device, &module);
if (ret) {
merr("fimc_is_sensor_g_module is fail(%d)", device, ret);
goto p_err;
}
/* preprocessor initialization need together with sensor init */
ret = fimc_is_sensor_mclk_off(&core->sensor[pdata->id], pdata->scenario, module->pdata->mclk_ch);
if (ret) {
err("fimc_is_sensor_mclk_off is fail(%d)", ret);
goto p_err;
}
if (!pdata->mclk_off) {
err("mclk_off is NULL");
ret = -EINVAL;
goto p_err;
}
ret = pdata->mclk_off(&device->pdev->dev, pdata->scenario, pdata->mclk_ch);
if (ret) {
err("mclk_off is fail(%d)", ret);
goto p_err;
}
clear_bit(FIMC_IS_PREPROC_MCLK_ON, &device->state);
p_err:
return ret;
}
static int fimc_is_preproc_iclk_on(struct fimc_is_device_preproc *device)
{
int ret = 0;
struct platform_device *pdev;
struct exynos_platform_fimc_is_preproc *pdata;
BUG_ON(!device);
BUG_ON(!device->pdev);
BUG_ON(!device->pdata);
pdev = device->pdev;
pdata = device->pdata;
if (test_bit(FIMC_IS_PREPROC_ICLK_ON, &device->state)) {
err("%s : already clk on", __func__);
goto p_err;
}
if (!pdata->iclk_cfg) {
err("iclk_cfg is NULL");
ret = -EINVAL;
goto p_err;
}
if (!pdata->iclk_on) {
err("iclk_on is NULL");
ret = -EINVAL;
goto p_err;
}
ret = pdata->iclk_cfg(&pdev->dev, pdata->scenario, 0);
if (ret) {
err("iclk_cfg is fail(%d)", ret);
goto p_err;
}
ret = pdata->iclk_on(&pdev->dev, pdata->scenario, 0);
if (ret) {
err("iclk_on is fail(%d)", ret);
goto p_err;
}
set_bit(FIMC_IS_PREPROC_ICLK_ON, &device->state);
p_err:
return ret;
}
static int fimc_is_preproc_iclk_off(struct fimc_is_device_preproc *device)
{
int ret = 0;
struct platform_device *pdev;
struct exynos_platform_fimc_is_preproc *pdata;
BUG_ON(!device);
BUG_ON(!device->pdev);
BUG_ON(!device->pdata);
pdev = device->pdev;
pdata = device->pdata;
if (!test_bit(FIMC_IS_PREPROC_ICLK_ON, &device->state)) {
err("%s : already clk off", __func__);
goto p_err;
}
if (!pdata->iclk_off) {
err("iclk_off is NULL");
ret = -EINVAL;
goto p_err;
}
ret = pdata->iclk_off(&pdev->dev, pdata->scenario, 0);
if (ret) {
err("iclk_off is fail(%d)", ret);
goto p_err;
}
clear_bit(FIMC_IS_PREPROC_ICLK_ON, &device->state);
p_err:
return ret;
}
static int fimc_is_preproc_gpio_on(struct fimc_is_device_preproc *device)
{
int ret = 0;
u32 scenario, gpio_scenario;
struct fimc_is_core *core;
struct fimc_is_module_enum *module;
struct fimc_is_vender *vender;
BUG_ON(!device);
BUG_ON(!device->pdev);
BUG_ON(!device->pdata);
BUG_ON(!device->private_data);
module = NULL;
gpio_scenario = GPIO_SCENARIO_ON;
core = device->private_data;
scenario = device->pdata->scenario;
vender = &core->vender;
if (test_bit(FIMC_IS_PREPROC_GPIO_ON, &device->state)) {
merr("%s : already gpio on", device, __func__);
goto p_err;
}
ret = fimc_is_preproc_g_module(device, &module);
if (ret) {
merr("fimc_is_sensor_g_module is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_vender_module_sel(vender, module);
if (ret) {
merr("fimc_is_vender_module_sel is fail(%d)", device, ret);
goto p_err;
}
if (!test_and_set_bit(FIMC_IS_MODULE_GPIO_ON, &module->state)) {
struct exynos_platform_fimc_is_module *pdata;
#ifdef CONFIG_COMPANION_USE
ret = fimc_is_vender_preprocessor_gpio_on_sel(vender,
scenario, &gpio_scenario);
if (ret) {
clear_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
merr("fimc_is_vender_preprocessor_gpio_on_sel is fail(%d)",
device, ret);
goto p_err;
}
#endif
pdata = module->pdata;
if (!pdata) {
clear_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
merr("pdata is NULL", device);
ret = -EINVAL;
goto p_err;
}
if (!pdata->gpio_cfg) {
clear_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
merr("gpio_cfg is NULL", device);
ret = -EINVAL;
goto p_err;
}
ret = pdata->gpio_cfg(module, scenario, gpio_scenario);
if (ret) {
clear_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
merr("gpio_cfg is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_vender_preprocessor_gpio_on(vender,
scenario, gpio_scenario);
if (ret) {
merr("fimc_is_vender_preprocessor_gpio_on is fail(%d)",
device, ret);
goto p_err;
}
}
set_bit(FIMC_IS_PREPROC_GPIO_ON, &device->state);
p_err:
return ret;
}
static int fimc_is_preproc_gpio_off(struct fimc_is_device_preproc *device)
{
int ret = 0;
u32 scenario, gpio_scenario;
struct fimc_is_core *core;
struct fimc_is_module_enum *module;
struct fimc_is_vender *vender;
BUG_ON(!device);
BUG_ON(!device->pdev);
BUG_ON(!device->pdata);
BUG_ON(!device->private_data);
module = NULL;
gpio_scenario = GPIO_SCENARIO_OFF;
core = device->private_data;
scenario = device->pdata->scenario;
vender = &core->vender;
if (!test_bit(FIMC_IS_PREPROC_GPIO_ON, &device->state)) {
err("%s : already gpio off", __func__);
goto p_err;
}
ret = fimc_is_preproc_g_module(device, &module);
if (ret) {
merr("fimc_is_sensor_g_module is fail(%d)", device, ret);
goto p_err;
}
if (test_and_clear_bit(FIMC_IS_MODULE_GPIO_ON, &module->state)) {
struct exynos_platform_fimc_is_module *pdata;
fimc_is_sensor_deinit_module(module);
ret = fimc_is_vender_preprocessor_gpio_off_sel(vender,
scenario, &gpio_scenario);
if (ret) {
set_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
merr("fimc_is_vender_preprocessor_gpio_off_sel is fail(%d)",
device, ret);
goto p_err;
}
pdata = module->pdata;
if (!pdata) {
set_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
merr("pdata is NULL", device);
ret = -EINVAL;
goto p_err;
}
if (!pdata->gpio_cfg) {
set_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
merr("gpio_cfg is NULL", device);
ret = -EINVAL;
goto p_err;
}
ret = pdata->gpio_cfg(module, scenario, gpio_scenario);
if (ret) {
set_bit(FIMC_IS_MODULE_GPIO_ON, &module->state);
merr("gpio_cfg is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_vender_preprocessor_gpio_off(vender,
scenario, gpio_scenario);
if (ret) {
merr("fimc_is_vender_preprocessor_gpio_off is fail(%d)",
device, ret);
goto p_err;
}
}
clear_bit(FIMC_IS_PREPROC_GPIO_ON, &device->state);
p_err:
if (module != NULL) {
fimc_is_vender_module_del(vender, module);
}
return ret;
}
int fimc_is_preproc_open(struct fimc_is_device_preproc *device,
struct fimc_is_video_ctx *vctx)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_vender *vender;
struct exynos_platform_fimc_is_preproc *pdata;
BUG_ON(!device);
BUG_ON(!device->pdata);
BUG_ON(!device->private_data);
core = device->private_data;
pdata = device->pdata;
vender = &core->vender;
if (test_bit(FIMC_IS_PREPROC_OPEN, &device->state)) {
err("already open");
ret = -EMFILE;
goto p_err;
}
ret = fimc_is_resource_get(device->resourcemgr, RESOURCE_TYPE_PREPROC);
if (ret) {
merr("fimc_is_resource_get is fail", device);
goto p_err;
}
device->vctx = vctx;
set_bit(FIMC_IS_PREPROC_OPEN, &device->state);
p_err:
minfo("[PRE:D] %s():%d\n", device, __func__, ret);
return ret;
}
int fimc_is_preproc_close(struct fimc_is_device_preproc *device)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_vender *vender;
struct exynos_platform_fimc_is_preproc *pdata;
BUG_ON(!device);
BUG_ON(!device->pdata);
BUG_ON(!device->private_data);
core = device->private_data;
pdata = device->pdata;
vender = &core->vender;
if (!test_bit(FIMC_IS_PREPROC_OPEN, &device->state)) {
err("already close");
ret = -EMFILE;
goto p_err;
}
ret = fimc_is_resource_put(device->resourcemgr, RESOURCE_TYPE_PREPROC);
if (ret)
merr("fimc_is_resource_put is fail(%d)", device, ret);
clear_bit(FIMC_IS_PREPROC_OPEN, &device->state);
clear_bit(FIMC_IS_PREPROC_S_INPUT, &device->state);
p_err:
minfo("[PRE:D] %s(%d)\n", device, __func__, ret);
return ret;
}
static int fimc_is_preproc_probe(struct platform_device *pdev)
{
int ret = 0;
u32 instance = -1;
atomic_t device_id;
struct fimc_is_core *core;
struct fimc_is_device_preproc *device;
struct exynos_platform_fimc_is_preproc *pdata;
BUG_ON(!pdev);
if (fimc_is_dev == NULL) {
warn("fimc_is_dev is not yet probed(preprocessor)");
pdev->dev.init_name = FIMC_IS_PREPROC_DEV_NAME;
return -EPROBE_DEFER;
}
core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev);
if (!core) {
err("core is NULL");
return -EINVAL;
}
#ifdef CONFIG_OF
ret = fimc_is_preprocessor_parse_dt(pdev);
if (ret) {
err("parsing device tree is fail(%d)", ret);
goto p_err;
}
#endif
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
err("pdata is NULL");
ret = -EINVAL;
goto p_err;
}
atomic_set(&device_id, 0);
device = &core->preproc;
memset(&device->v4l2_dev, 0, sizeof(struct v4l2_device));
instance = v4l2_device_set_name(&device->v4l2_dev, "exynos-fimc-is-preprocessor", &device_id);
device->instance = instance;
device->resourcemgr = &core->resourcemgr;
device->pdev = pdev;
device->private_data = core;
device->regs = core->regs;
device->pdata = pdata;
platform_set_drvdata(pdev, device);
device_init_wakeup(&pdev->dev, true);
/* init state */
clear_bit(FIMC_IS_PREPROC_OPEN, &device->state);
clear_bit(FIMC_IS_PREPROC_MCLK_ON, &device->state);
clear_bit(FIMC_IS_PREPROC_ICLK_ON, &device->state);
clear_bit(FIMC_IS_PREPROC_GPIO_ON, &device->state);
clear_bit(FIMC_IS_PREPROC_S_INPUT, &device->state);
ret = v4l2_device_register(&pdev->dev, &device->v4l2_dev);
if (ret) {
err("v4l2_device_register is fail(%d)", ret);
goto p_err;
}
ret = fimc_is_pre_video_probe(device);
if (ret) {
err("fimc_is_preproc_video_probe is fail(%d)", ret);
v4l2_device_unregister(&device->v4l2_dev);
goto p_err;
}
#if defined(CONFIG_PM_RUNTIME)
pm_runtime_enable(&pdev->dev);
#endif
p_err:
info("[%d][PRE:D] %s():%d\n", instance, __func__, ret);
return ret;
}
static int fimc_is_preproc_remove(struct platform_device *pdev)
{
int ret = 0;
info("%s\n", __func__);
return ret;
}
static int fimc_is_preproc_suspend(struct device *dev)
{
int ret = 0;
info("%s\n", __func__);
return ret;
}
static int fimc_is_preproc_resume(struct device *dev)
{
int ret = 0;
info("%s\n", __func__);
return ret;
}
int fimc_is_preproc_runtime_suspend(struct device *dev)
{
int ret = 0;
struct platform_device *pdev = to_platform_device(dev);
struct fimc_is_device_preproc *device;
device = (struct fimc_is_device_preproc *)platform_get_drvdata(pdev);
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
/* gpio uninit */
ret = fimc_is_preproc_gpio_off(device);
if (ret) {
err("fimc_is_preproc_gpio_off is fail(%d)", ret);
goto p_err;
}
/* periperal internal clock off */
ret = fimc_is_preproc_iclk_off(device);
if (ret) {
err("fimc_is_preproc_iclk_off is fail(%d)", ret);
goto p_err;
}
/* master clock off */
ret = fimc_is_preproc_mclk_off(device);
if (ret) {
err("fimc_is_preproc_mclk_off is fail(%d)", ret);
goto p_err;
}
device->module = NULL;
p_err:
info("[PRE:D] %s():%d\n", __func__, ret);
return ret;
}
int fimc_is_preproc_runtime_resume(struct device *dev)
{
int ret = 0;
struct platform_device *pdev = to_platform_device(dev);
struct fimc_is_device_preproc *device;
device = (struct fimc_is_device_preproc *)platform_get_drvdata(pdev);
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
/* periperal internal clock on */
ret = fimc_is_preproc_iclk_on(device);
if (ret) {
err("fimc_is_preproc_iclk_on is fail(%d)", ret);
goto p_err;
}
p_err:
info("[PRE:D] %s():%d\n", __func__, ret);
return ret;
}
int fimc_is_preproc_s_input(struct fimc_is_device_preproc *device,
u32 input,
u32 scenario)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_vender *vender;
struct fimc_is_device_sensor *device_sensor;
BUG_ON(!device);
BUG_ON(!device->pdata);
BUG_ON(!device->private_data);
BUG_ON(input >= SENSOR_NAME_END);
core = device->private_data;
vender = &core->vender;
device_sensor = &core->sensor[device->pdata->id];
ret = fimc_is_search_sensor_module(device_sensor, input, &device->module);
if (ret) {
err("fimc_is_search_sensor_module is fail(%d)", ret);
goto p_err;
}
core->current_position = device->module->position;
if (test_bit(FIMC_IS_PREPROC_S_INPUT, &device->state)) {
err("already s_input");
ret = -EMFILE;
goto p_err;
}
ret = fimc_is_preproc_mclk_on(device);
if (ret) {
err("fimc_is_preproc_mclk_on is fail(%d)", ret);
goto p_err;
}
/* gpio init */
ret = fimc_is_preproc_gpio_on(device);
if (ret) {
err("fimc_is_preproc_gpio_on is fail(%d)", ret);
goto p_err;
}
ret = fimc_is_vender_fw_prepare(vender);
if (ret) {
err("fimc_is_vender_fw_prepare is fail(%d)", ret);
goto p_err;
}
#ifdef CONFIG_COMPANION_USE
ret = fimc_is_vender_preproc_fw_load(vender);
if (ret) {
err("fimc_is_vender_preproc_fw_load is fail(%d)", ret);
goto p_err;
}
#endif
set_bit(FIMC_IS_PREPROC_S_INPUT, &device->state);
p_err:
minfo("[PRE:D] %s(%d, %d):%d\n", device, __func__, scenario, input, ret);
return ret;
}
#ifdef CONFIG_COMPANION_FACTORY_VALIDATION
int fimc_is_preproc_fac_valid_check(struct fimc_is_device_preproc *device,
u32 input,
u32 scenario)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_vender *vender;
struct fimc_is_device_sensor *device_sensor;
BUG_ON(!device);
BUG_ON(!device->pdata);
BUG_ON(!device->private_data);
BUG_ON(input >= SENSOR_NAME_END);
core = device->private_data;
vender = &core->vender;
device_sensor = &core->sensor[device->pdata->id];
ret = fimc_is_search_sensor_module(device_sensor, input, &device->module);
if (ret) {
err("fimc_is_search_sensor_module is fail(%d)", ret);
goto p_err;
}
core->current_position = device->module->position;
ret = fimc_is_preproc_mclk_on(device);
if (ret) {
err("fimc_is_preproc_mclk_on is fail(%d)", ret);
goto p_err;
}
ret = fimc_is_preproc_gpio_on(device);
if (ret) {
err("fimc_is_preproc_gpio_on is fail(%d)", ret);
fimc_is_preproc_mclk_off(device);
goto p_err;
}
msleep(150);
ret = fimc_is_comp_is_valid_fac(core);
if (ret) {
err("fimc_is_comp_is_valid is fail(%d)", ret);
fimc_is_preproc_gpio_off(device);
fimc_is_preproc_mclk_off(device);
goto p_err;
}
ret = fimc_is_preproc_gpio_off(device);
if (ret) {
err("fimc_is_preproc_gpio_off is fail(%d)", ret);
fimc_is_preproc_mclk_off(device);
goto p_err;
}
ret = fimc_is_preproc_mclk_off(device);
if (ret) {
err("fimc_is_preproc_mclk_off is fail(%d)", ret);
goto p_err;
}
p_err:
minfo("[PRE:D] %s(%d, %d):%d\n", device, __func__, scenario, input, ret);
return ret;
}
#endif
static const struct dev_pm_ops fimc_is_preproc_pm_ops = {
.suspend = fimc_is_preproc_suspend,
.resume = fimc_is_preproc_resume,
.runtime_suspend = fimc_is_preproc_runtime_suspend,
.runtime_resume = fimc_is_preproc_runtime_resume,
};
#ifdef CONFIG_OF
static const struct of_device_id exynos_fimc_is_preproc_match[] = {
{
.compatible = "samsung,exynos5-fimc-is-preprocessor",
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_fimc_is_preproc_match);
static struct platform_driver fimc_is_preproc_driver = {
.probe = fimc_is_preproc_probe,
.remove = fimc_is_preproc_remove,
.driver = {
.name = FIMC_IS_PREPROC_DEV_NAME,
.owner = THIS_MODULE,
.pm = &fimc_is_preproc_pm_ops,
.of_match_table = exynos_fimc_is_preproc_match,
}
};
#else
static struct platform_device_id fimc_is_preproc_driver_ids[] = {
{
.name = FIMC_IS_PREPROC_DEV_NAME,
.driver_data = 0,
},
{},
};
MODULE_DEVICE_TABLE(platform, fimc_is_preproc_driver_ids);
static struct platform_driver fimc_is_preproc_driver = {
.probe = fimc_is_preproc_probe,
.remove = __devexit_p(fimc_is_preproc_remove),
.id_table = fimc_is_preproc_driver_ids,
.driver = {
.name = FIMC_IS_PREPROC_DEV_NAME,
.owner = THIS_MODULE,
.pm = &fimc_is_preproc_pm_ops,
}
};
#endif
static int __init fimc_is_preproc_init(void)
{
int ret = platform_driver_register(&fimc_is_preproc_driver);
if (ret)
err("platform_driver_register failed: %d\n", ret);
return ret;
}
late_initcall(fimc_is_preproc_init);
static void __exit fimc_is_preproc_exit(void)
{
platform_driver_unregister(&fimc_is_preproc_driver);
}
module_exit(fimc_is_preproc_exit);
MODULE_AUTHOR("Wooki Min<wooki.min@samsung.com>");
MODULE_DESCRIPTION("Exynos FIMC_IS_PREPROCESSOR driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,70 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DEVICE_PREPROCESSOR_H
#define FIMC_IS_DEVICE_PREPROCESSOR_H
#include <linux/interrupt.h>
#include <exynos-fimc-is-sensor.h>
#include <exynos-fimc-is-preprocessor.h>
#include "fimc-is-video.h"
#include "fimc-is-config.h"
#define FIMC_IS_PREPROC_DEV_NAME "exynos-fimc-is-preprocessor"
#define PREPROC_SCENARIO_MASK 0xF0000000
#define PREPROC_SCENARIO_SHIFT 28
#define PREPROC_MODULE_MASK 0x0FFFFFFF
#define PREPROC_MODULE_SHIFT 0
#define MAX_PREPROC_S_INPUT_WAITING 2000 /* 2 second */
enum fimc_is_preproc_state {
FIMC_IS_PREPROC_OPEN,
FIMC_IS_PREPROC_MCLK_ON,
FIMC_IS_PREPROC_ICLK_ON,
FIMC_IS_PREPROC_GPIO_ON,
FIMC_IS_PREPROC_S_INPUT
};
struct fimc_is_device_preproc {
struct v4l2_device v4l2_dev;
struct platform_device *pdev;
void __iomem *regs;
struct fimc_is_mem mem;
u32 instance;
struct fimc_is_video_ctx *vctx;
struct fimc_is_video video;
unsigned long state;
struct fimc_is_resourcemgr *resourcemgr;
struct exynos_platform_fimc_is_preproc *pdata;
struct fimc_is_module_enum *module;
void *private_data;
};
int fimc_is_preproc_open(struct fimc_is_device_preproc *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_preproc_close(struct fimc_is_device_preproc *device);
int fimc_is_preproc_s_input(struct fimc_is_device_preproc *device,
u32 input,
u32 scenario);
int fimc_is_preproc_g_module(struct fimc_is_device_preproc *device,
struct fimc_is_module_enum **module);
int fimc_is_preproc_runtime_suspend(struct device *dev);
int fimc_is_preproc_runtime_resume(struct device *dev);
#ifdef CONFIG_COMPANION_FACTORY_VALIDATION
int fimc_is_preproc_fac_valid_check(struct fimc_is_device_preproc *device,
u32 input,
u32 scenario);
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,367 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DEVICE_SENSOR_H
#define FIMC_IS_DEVICE_SENSOR_H
#include <dt-bindings/camera/fimc_is.h>
#include <exynos-fimc-is-sensor.h>
#include "fimc-is-mem.h"
#include "fimc-is-video.h"
#include "fimc-is-resourcemgr.h"
#include "fimc-is-groupmgr.h"
#include "fimc-is-device-flite.h"
#include "fimc-is-device-csi.h"
#include "fimc-is-helper-i2c.h"
struct fimc_is_video_ctx;
struct fimc_is_device_ischain;
#define EXPECT_FRAME_START 0
#define EXPECT_FRAME_END 1
#define LOG_INTERVAL_OF_DROPS 30
#define FLITE_NOTIFY_FSTART 0
#define FLITE_NOTIFY_FEND 1
#define FLITE_NOTIFY_DMA_END 2
#define CSIS_NOTIFY_FSTART 3
#define CSIS_NOTIFY_FEND 4
#define CSIS_NOTIFY_DMA_END 5
#define CSIS_NOTIFY_LINE 6
#define SENSOR_MAX_ENUM 20
#define SENSOR_DEFAULT_FRAMERATE 30
#define SENSOR_MODE_MASK 0xFFFF0000
#define SENSOR_MODE_SHIFT 16
#define SENSOR_MODE_DEINIT 0xFFFF
#define SENSOR_SCENARIO_MASK 0xF0000000
#define SENSOR_SCENARIO_SHIFT 28
#define ASB_SCENARIO_MASK 0xF000
#define ASB_SCENARIO_SHIFT 12
#define SENSOR_MODULE_MASK 0x00000FFF
#define SENSOR_MODULE_SHIFT 0
#define SENSOR_SSTREAM_MASK 0x0000000F
#define SENSOR_SSTREAM_SHIFT 0
#define SENSOR_INSTANT_MASK 0x0FFF0000
#define SENSOR_INSTANT_SHIFT 16
#define SENSOR_NOBLOCK_MASK 0xF0000000
#define SENSOR_NOBLOCK_SHIFT 28
#define SENSOR_I2C_CH_MASK 0xFF
#define SENSOR_I2C_CH_SHIFT 0
#define ACTUATOR_I2C_CH_MASK 0xFF00
#define ACTUATOR_I2C_CH_SHIFT 8
#define OIS_I2C_CH_MASK 0xFF0000
#define OIS_I2C_CH_SHIFT 16
#define SENSOR_I2C_ADDR_MASK 0xFF
#define SENSOR_I2C_ADDR_SHIFT 0
#define ACTUATOR_I2C_ADDR_MASK 0xFF00
#define ACTUATOR_I2C_ADDR_SHIFT 8
#define OIS_I2C_ADDR_MASK 0xFF0000
#define OIS_I2C_ADDR_SHIFT 16
#define SENSOR_SIZE_WIDTH_MASK 0xFFFF0000
#define SENSOR_SIZE_WIDTH_SHIFT 16
#define SENSOR_SIZE_HEIGHT_MASK 0xFFFF
#define SENSOR_SIZE_HEIGHT_SHIFT 0
#define FIMC_IS_TIMESTAMP_HASH_KEY 10
#define FIMC_IS_SENSOR_CFG_EXT(w, h, f, s, m, l, ls) { \
.width = w, \
.height = h, \
.framerate = f, \
.settle = s, \
.mode = m, \
.lanes = l, \
.mipi_speed = ls, \
}
#define FIMC_IS_SENSOR_CFG(w, h, f, s, m, l) { \
.width = w, \
.height = h, \
.framerate = f, \
.settle = s, \
.mode = m, \
.lanes = l, \
.mipi_speed = 0, \
}
enum fimc_is_sensor_subdev_ioctl {
SENSOR_IOCTL_DMA_CANCEL,
};
enum fimc_is_sensor_output_entity {
FIMC_IS_SENSOR_OUTPUT_NONE = 0,
FIMC_IS_SENSOR_OUTPUT_FRONT,
};
enum fimc_is_sensor_force_stop {
FIMC_IS_BAD_FRAME_STOP = 0,
FIMC_IS_MIF_THROTTLING_STOP = 1,
FIMC_IS_FLITE_OVERFLOW_STOP = 2
};
enum fimc_is_module_state {
FIMC_IS_MODULE_GPIO_ON,
FIMC_IS_MODULE_STANDBY_ON
};
struct fimc_is_sensor_cfg {
u32 width;
u32 height;
u32 framerate;
u32 settle;
int mode;
u32 lanes;
u32 mipi_speed;
};
struct fimc_is_sensor_ops {
int (*stream_on)(struct v4l2_subdev *subdev);
int (*stream_off)(struct v4l2_subdev *subdev);
int (*s_duration)(struct v4l2_subdev *subdev, u64 duration);
int (*g_min_duration)(struct v4l2_subdev *subdev);
int (*g_max_duration)(struct v4l2_subdev *subdev);
int (*s_exposure)(struct v4l2_subdev *subdev, u64 exposure);
int (*g_min_exposure)(struct v4l2_subdev *subdev);
int (*g_max_exposure)(struct v4l2_subdev *subdev);
int (*s_again)(struct v4l2_subdev *subdev, u64 sensivity);
int (*g_min_again)(struct v4l2_subdev *subdev);
int (*g_max_again)(struct v4l2_subdev *subdev);
int (*s_dgain)(struct v4l2_subdev *subdev);
int (*g_min_dgain)(struct v4l2_subdev *subdev);
int (*g_max_dgain)(struct v4l2_subdev *subdev);
};
struct fimc_is_module_enum {
u32 sensor_id;
struct v4l2_subdev *subdev; /* connected module subdevice */
u32 device; /* connected sensor device */
unsigned long state;
u32 pixel_width;
u32 pixel_height;
u32 active_width;
u32 active_height;
u32 margin_left;
u32 margin_right;
u32 margin_top;
u32 margin_bottom;
u32 max_framerate;
u32 position;
u32 mode;
u32 lanes;
u32 bitwidth;
u32 vcis;
struct fimc_is_vci *vci;
u32 cfgs;
struct fimc_is_sensor_cfg *cfg;
struct i2c_client *client;
struct sensor_open_extended ext;
struct fimc_is_sensor_ops *ops;
char *sensor_maker;
char *sensor_name;
char *setfile_name;
void *private_data;
struct exynos_platform_fimc_is_module *pdata;
struct device *dev;
};
enum fimc_is_sensor_state {
FIMC_IS_SENSOR_PROBE,
FIMC_IS_SENSOR_OPEN,
FIMC_IS_SENSOR_MCLK_ON,
FIMC_IS_SENSOR_ICLK_ON,
FIMC_IS_SENSOR_GPIO_ON,
FIMC_IS_SENSOR_S_INPUT,
FIMC_IS_SENSOR_S_CONFIG,
FIMC_IS_SENSOR_DRIVING,
FIMC_IS_SENSOR_FRONT_START,
FIMC_IS_SENSOR_FRONT_DTP_STOP,
FIMC_IS_SENSOR_BACK_START,
FIMC_IS_SENSOR_BACK_NOWAIT_STOP,
FIMC_IS_SENSOR_OTF_OUTPUT,
};
struct fimc_is_device_sensor {
struct v4l2_device v4l2_dev;
struct platform_device *pdev;
struct fimc_is_mem mem;
u32 instance;
u32 position;
struct fimc_is_image image;
struct fimc_is_video_ctx *vctx;
struct fimc_is_video video;
struct fimc_is_device_ischain *ischain;
struct fimc_is_groupmgr *groupmgr;
struct fimc_is_resourcemgr *resourcemgr;
struct fimc_is_devicemgr *devicemgr;
struct fimc_is_module_enum module_enum[SENSOR_MAX_ENUM];
struct fimc_is_sensor_cfg *cfg;
/* current control value */
struct camera2_sensor_ctl sensor_ctl;
struct camera2_lens_ctl lens_ctl;
struct camera2_flash_ctl flash_ctl;
struct work_struct control_work;
struct fimc_is_frame *control_frame;
u64 timestamp[FIMC_IS_TIMESTAMP_HASH_KEY];
u64 timestampboot[FIMC_IS_TIMESTAMP_HASH_KEY];
u32 fcount;
u32 line_fcount;
u32 instant_cnt;
int instant_ret;
wait_queue_head_t instant_wait;
struct work_struct instant_work;
unsigned long state;
spinlock_t slock_state;
atomic_t group_open_cnt;
/* hardware configuration */
struct v4l2_subdev *subdev_module;
struct v4l2_subdev *subdev_csi;
struct v4l2_subdev *subdev_flite;
/* sensor dma video node */
struct fimc_is_video video_ssxvc0;
struct fimc_is_video video_ssxvc1;
struct fimc_is_video video_ssxvc2;
struct fimc_is_video video_ssxvc3;
/* subdev for dma */
struct fimc_is_subdev ssvc0;
struct fimc_is_subdev ssvc1;
struct fimc_is_subdev ssvc2;
struct fimc_is_subdev ssvc3;
struct fimc_is_subdev bns;
/* gain boost */
int min_target_fps;
int max_target_fps;
int scene_mode;
/* for vision control */
int exposure_time;
u64 frame_duration;
/* ENABLE_DTP */
bool dtp_check;
struct timer_list dtp_timer;
unsigned long force_stop;
/* for early buffer done */
u32 early_buf_done_mode;
struct hrtimer early_buf_timer;
struct exynos_platform_fimc_is_sensor *pdata;
void *private_data;
struct fimc_is_group group_sensor;
struct fimc_is_path_info path;
u32 sensor_width;
u32 sensor_height;
};
int fimc_is_sensor_open(struct fimc_is_device_sensor *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_sensor_close(struct fimc_is_device_sensor *device);
#ifdef CONFIG_USE_SENSOR_GROUP
int fimc_is_sensor_s_input(struct fimc_is_device_sensor *device,
u32 input,
u32 scenario,
u32 video_id);
#else
int fimc_is_sensor_s_input(struct fimc_is_device_sensor *device,
u32 input,
u32 scenario);
#endif
int fimc_is_sensor_s_ctrl(struct fimc_is_device_sensor *device,
struct v4l2_control *ctrl);
int fimc_is_sensor_subdev_buffer_queue(struct fimc_is_device_sensor *device,
enum fimc_is_subdev_id subdev_id,
u32 index);
int fimc_is_sensor_buffer_queue(struct fimc_is_device_sensor *device,
struct fimc_is_queue *queue,
u32 index);
int fimc_is_sensor_buffer_finish(struct fimc_is_device_sensor *device,
u32 index);
int fimc_is_sensor_front_start(struct fimc_is_device_sensor *device,
u32 instant_cnt,
u32 nonblock);
int fimc_is_sensor_front_stop(struct fimc_is_device_sensor *device);
int fimc_is_sensor_s_framerate(struct fimc_is_device_sensor *device,
struct v4l2_streamparm *param);
int fimc_is_sensor_s_bns(struct fimc_is_device_sensor *device,
u32 reatio);
int fimc_is_sensor_s_frame_duration(struct fimc_is_device_sensor *device,
u32 frame_duration);
int fimc_is_sensor_s_exposure_time(struct fimc_is_device_sensor *device,
u32 exposure_time);
int fimc_is_sensor_s_fcount(struct fimc_is_device_sensor *device);
struct fimc_is_sensor_cfg * fimc_is_sensor_g_mode(struct fimc_is_device_sensor *device);
int fimc_is_sensor_mclk_on(struct fimc_is_device_sensor *device, u32 scenario, u32 channel);
int fimc_is_sensor_mclk_off(struct fimc_is_device_sensor *device, u32 scenario, u32 channel);
int fimc_is_sensor_gpio_on(struct fimc_is_device_sensor *device);
int fimc_is_sensor_gpio_off(struct fimc_is_device_sensor *device);
int fimc_is_sensor_gpio_dbg(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_ctrl(struct fimc_is_device_sensor *device,
struct v4l2_control *ctrl);
int fimc_is_sensor_g_instance(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_framerate(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_fcount(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_width(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_height(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_bns_width(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_bns_height(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_bns_ratio(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_bratio(struct fimc_is_device_sensor *device);
int fimc_is_sensor_g_module(struct fimc_is_device_sensor *device,
struct fimc_is_module_enum **module);
int fimc_is_sensor_deinit_module(struct fimc_is_module_enum *module);
int fimc_is_sensor_g_position(struct fimc_is_device_sensor *device);
int fimc_is_search_sensor_module(struct fimc_is_device_sensor *device,
u32 sensor_id, struct fimc_is_module_enum **module);
int fimc_is_sensor_dm_tag(struct fimc_is_device_sensor *device,
struct fimc_is_frame *frame);
int fimc_is_sensor_buf_tag(struct fimc_is_device_sensor *device,
struct fimc_is_subdev *f_subdev,
struct v4l2_subdev *v_subdev,
struct fimc_is_frame *ldr_frame);
int fimc_is_sensor_g_csis_error(struct fimc_is_device_sensor *device);
int fimc_is_sensor_group_tag(struct fimc_is_device_sensor *device,
struct fimc_is_frame *frame,
struct camera2_node *ldr_node);
int fimc_is_sensor_dma_cancel(struct fimc_is_device_sensor *device);
extern const struct fimc_is_queue_ops fimc_is_sensor_ops;
extern const struct fimc_is_queue_ops fimc_is_sensor_subdev_ops;
#define CALL_MOPS(s, op, args...) (((s)->ops->op) ? ((s)->ops->op(args)) : 0)
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,521 @@
/*
* Samsung Exynos SoC series FIMC-IS driver
*
* exynos5 fimc-is group manager functions
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include "fimc-is-hw.h"
#include "fimc-is-core.h"
#include "fimc-is-type.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
#include "fimc-is-framemgr.h"
#include "fimc-is-groupmgr.h"
#include "fimc-is-devicemgr.h"
#include "fimc-is-device-ischain.h"
#include "fimc-is-hw-control.h"
#ifdef CONFIG_USE_SENSOR_GROUP
static void tasklet_sensor_tag(unsigned long data)
{
int ret = 0;
u32 stream;
unsigned long flags;
struct fimc_is_framemgr *framemgr;
struct v4l2_subdev *subdev;
struct camera2_node ldr_node = {0, };
struct fimc_is_device_sensor *sensor;
struct devicemgr_sensor_tag_data *tag_data;
struct fimc_is_group *group;
struct fimc_is_frame *frame;
struct fimc_is_group_task *gtask;
tag_data = (struct devicemgr_sensor_tag_data *)data;
stream = tag_data->stream;
sensor = tag_data->devicemgr->sensor[stream];
group = tag_data->group;
subdev = sensor->subdev_csi;
gtask = &sensor->groupmgr->gtask[group->id];
#ifdef DBG_STREAMING
mginfo("[F%d] DEVICE TASKLET start\n", group->device, group, tag_data->fcount);
#endif
if (unlikely(test_bit(FIMC_IS_GROUP_FORCE_STOP, &group->state))) {
mgwarn(" cancel by fstop", group, group);
goto p_err;
}
if (unlikely(test_bit(FIMC_IS_GTASK_REQUEST_STOP, &gtask->state))) {
mgerr(" cancel by gstop", group, group);
goto p_err;
}
framemgr = GET_HEAD_GROUP_FRAMEMGR(group);
if (!framemgr) {
merr("framemgr is NULL", group);
return;
}
framemgr_e_barrier(framemgr, 0);
frame = find_frame(framemgr, FS_PROCESS, frame_fcount, (void *)(ulong)tag_data->fcount);
framemgr_x_barrier(framemgr, 0);
if (!frame) {
frame_manager_print_queues(framemgr);
merr("[F%d] There's no frame in processing." \
"Can't sync sensor and ischain buffer anymore..",
group, tag_data->fcount);
return;
}
ldr_node = frame->shot_ext->node_group.leader;
ret = fimc_is_sensor_group_tag(sensor, frame, &ldr_node);
if (ret) {
merr("fimc_is_sensor_group_tag is fail(%d)", group, ret);
goto p_err;
}
if (sensor->fcount >= frame->fcount) {
merr("late sensor tag. DMA will be canceled. (%d != %d)",
group, sensor->fcount, frame->fcount);
fimc_is_sensor_dma_cancel(sensor);
mginfo("[F%d] Start CANCEL Other subdev frame\n", group->device, group, frame->fcount);
flags = fimc_is_group_lock(group, group->device_type, false);
fimc_is_group_subdev_cancel(group, frame, group->device_type, FS_PROCESS, true);
fimc_is_group_unlock(group, flags, group->device_type, false);
mginfo("[F%d] End CANCEL Other subdev frame\n", group->device, group, frame->fcount);
}
#ifdef DBG_STREAMING
mginfo("[F%d] DEVICE TASKLET end\n", group->device, group, tag_data->fcount);
#endif
p_err:
return;
}
int fimc_is_devicemgr_probe(struct fimc_is_devicemgr *devicemgr)
{
int ret = 0;
return ret;
}
int fimc_is_devicemgr_open(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type)
{
int ret = 0;
u32 stream;
u32 group_id;
struct fimc_is_core *core;
struct fimc_is_group *group;
struct fimc_is_device_sensor *sensor;
struct fimc_is_device_ischain *ischain;
BUG_ON(!devicemgr);
BUG_ON(!device);
switch (type) {
case FIMC_IS_DEVICE_SENSOR:
sensor = (struct fimc_is_device_sensor *)device;
group = &sensor->group_sensor;
core = sensor->private_data;
BUG_ON(!core);
BUG_ON(!group);
/* get the stream id */
for (stream = 0; stream < FIMC_IS_STREAM_COUNT; ++stream) {
ischain = &core->ischain[stream];
if (!test_bit(FIMC_IS_ISCHAIN_OPEN, &ischain->state))
break;
}
BUG_ON(stream >= FIMC_IS_STREAM_COUNT);
/* init group's device information */
devicemgr->sensor[stream] = sensor;
group->instance = stream;
group->device = ischain;
group_id = GROUP_ID_SS0 + GET_SSX_ID(GET_VIDEO(sensor->vctx));
ret = fimc_is_group_open(sensor->groupmgr,
group,
group_id,
sensor->vctx);
if (ret) {
merr("fimc_is_group_open is fail(%d)", ischain, ret);
ret = -EINVAL;
goto p_err;
}
sensor->vctx->next_device = ischain;
break;
case FIMC_IS_DEVICE_ISCHAIN:
ischain = (struct fimc_is_device_ischain *)device;
devicemgr->ischain[stream] = ischain;
break;
default:
mgerr("device type(%d) is invalid", group, group, group->device_type);
BUG();
break;
}
p_err:
return ret;
}
int fimc_is_devicemgr_binding(struct fimc_is_devicemgr *devicemgr,
struct fimc_is_device_sensor *sensor,
struct fimc_is_device_ischain *ischain,
enum fimc_is_device_type type)
{
int ret = 0;
struct fimc_is_group *group;
struct fimc_is_group *child_group;
switch (type) {
case FIMC_IS_DEVICE_SENSOR:
if (test_bit(FIMC_IS_SENSOR_DRIVING, &sensor->state)) {
/* in case of sensor driving */
ischain->sensor = sensor;
sensor->ischain = ischain;
/*
* Forcely set the ischain's state to "Opened"
* Because if it's sensor driving mode, ischain was not opened from HAL.
*/
set_bit(FIMC_IS_ISCHAIN_OPEN, &ischain->state);
ret = fimc_is_groupmgr_init(sensor->groupmgr, ischain);
if (ret) {
merr("fimc_is_groupmgr_init is fail(%d)", ischain, ret);
ret = -EINVAL;
goto p_err;
}
}
break;
case FIMC_IS_DEVICE_ISCHAIN:
if (sensor &&
!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &ischain->state)) {
group = &sensor->group_sensor;
BUG_ON(group->instance != ischain->instance);
child_group = GET_HEAD_GROUP_IN_DEVICE(FIMC_IS_DEVICE_ISCHAIN, group);
if (child_group) {
info("[%d/%d] sensor otf output set",
sensor->instance, ischain->instance);
set_bit(FIMC_IS_SENSOR_OTF_OUTPUT, &sensor->state);
}
}
break;
default:
mgerr("device type(%d) is invalid", group, group, group->device_type);
BUG();
break;
}
p_err:
return ret;
}
int fimc_is_devicemgr_start(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type)
{
int ret = 0;
struct fimc_is_group *group;
struct fimc_is_group *child_group;
struct fimc_is_device_sensor *sensor;
struct devicemgr_sensor_tag_data *tag_data;
u32 stream;
int i;
switch (type) {
case FIMC_IS_DEVICE_SENSOR:
sensor = (struct fimc_is_device_sensor *)device;
group = &sensor->group_sensor;
child_group = GET_HEAD_GROUP_IN_DEVICE(FIMC_IS_DEVICE_ISCHAIN, group);
stream = group->instance;
if (!test_bit(FIMC_IS_SENSOR_DRIVING, &sensor->state) && sensor->ischain) {
ret = fimc_is_ischain_start_wrap(sensor->ischain, group);
if (ret) {
merr("fimc_is_ischain_start_wrap is fail(%d)", sensor->ischain, ret);
ret = -EINVAL;
goto p_err;
}
} else {
ret = fimc_is_groupmgr_start(sensor->groupmgr, group->device);
if (ret) {
merr("fimc_is_groupmgr_start is fail(%d)", group->device, ret);
ret = -EINVAL;
goto p_err;
}
}
/* Only in case of OTF case, used tasklet. */
if (sensor->ischain && child_group) {
for (i = 0; i < TAG_DATA_MAX; i++) {
tag_data = &devicemgr->sensor_tag_data[stream][i];
tasklet_init(&devicemgr->tasklet_sensor_tag[stream][i], tasklet_sensor_tag, (unsigned long)tag_data);
}
}
break;
case FIMC_IS_DEVICE_ISCHAIN:
break;
default:
mgerr("device type(%d) is invalid", group, group, group->device_type);
BUG();
break;
}
p_err:
return ret;
}
int fimc_is_devicemgr_stop(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type)
{
int ret = 0;
struct fimc_is_group *group;
struct fimc_is_device_sensor *sensor;
switch (type) {
case FIMC_IS_DEVICE_SENSOR:
sensor = (struct fimc_is_device_sensor *)device;
group = &sensor->group_sensor;
if (!test_bit(FIMC_IS_SENSOR_DRIVING, &sensor->state) && sensor->ischain) {
ret = fimc_is_ischain_stop_wrap(sensor->ischain, group);
if (ret) {
merr("fimc_is_ischain_stop_wrap is fail(%d)", sensor->ischain, ret);
ret = -EINVAL;
goto p_err;
}
} else {
ret = fimc_is_groupmgr_stop(sensor->groupmgr, group->device);
if (ret) {
merr("fimc_is_groupmgr_stop is fail(%d)", group->device, ret);
ret = -EINVAL;
goto p_err;
}
}
break;
case FIMC_IS_DEVICE_ISCHAIN:
break;
default:
mgerr("device type(%d) is invalid", group, group, group->device_type);
BUG();
break;
}
p_err:
return ret;
}
int fimc_is_devicemgr_close(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type)
{
int ret = 0;
struct fimc_is_group *group;
struct fimc_is_device_sensor *sensor;
switch (type) {
case FIMC_IS_DEVICE_SENSOR:
sensor = (struct fimc_is_device_sensor *)device;
BUG_ON(!sensor);
/*
* Forcely set the ischain's state to "Not Opened"
* Because if it's sensor driving mode, ischain was not opened from HAL.
*/
if (test_bit(FIMC_IS_SENSOR_DRIVING, &sensor->state) && sensor->ischain)
clear_bit(FIMC_IS_ISCHAIN_OPEN, &sensor->ischain->state);
break;
case FIMC_IS_DEVICE_ISCHAIN:
break;
default:
mgerr("device type(%d) is invalid", group, group, group->device_type);
BUG();
break;
}
return ret;
}
int fimc_is_devicemgr_shot_callback(struct fimc_is_group *group,
struct fimc_is_frame *frame,
u32 fcount,
enum fimc_is_device_type type)
{
int ret = 0;
struct fimc_is_group *child_group;
struct camera2_node ldr_node = {0, };
struct fimc_is_devicemgr *devicemgr;
struct devicemgr_sensor_tag_data *tag_data;
u32 stream;
u32 index;
switch (type) {
case FIMC_IS_DEVICE_SENSOR:
child_group = GET_HEAD_GROUP_IN_DEVICE(FIMC_IS_DEVICE_ISCHAIN, group);
PROGRAM_COUNT(9);
#ifdef DBG_STREAMING
mgrinfo(" DEVICE SHOT CALLBACK(%d) %s\n", group->device,
group, frame, frame->index, child_group ? "OTF" : "M2M");
#endif
/* OTF */
if (child_group) {
child_group->shot_callback(child_group->device, frame);
/* M2M */
} else {
#ifdef DBG_STREAMING
mginfo("[F%d] DEVICE TASKLET M2M\n", group->device, group, frame->fcount);
#endif
ret = fimc_is_sensor_group_tag(group->device->sensor, frame, &ldr_node);
if (ret) {
merr("fimc_is_sensor_group_tag is fail(%d)", group, ret);
ret = -EINVAL;
goto p_err;
}
}
PROGRAM_COUNT(10);
break;
case FIMC_IS_DEVICE_ISCHAIN:
devicemgr = group->device->devicemgr;
index = fcount % TAG_DATA_MAX;
stream = group->instance;
tag_data = &devicemgr->sensor_tag_data[stream][index];
tag_data->fcount = fcount;
tag_data->devicemgr = devicemgr;
tag_data->group = &devicemgr->sensor[stream]->group_sensor;
tag_data->stream = stream;
/* OTF */
if (frame->type == SHOT_TYPE_EXTERNAL &&
group->head->device_type == FIMC_IS_DEVICE_SENSOR) {
#ifdef DBG_STREAMING
mginfo("[F%d] DEVICE TASKLET schedule\n", group->device, group, fcount);
#endif
tasklet_schedule(&devicemgr->tasklet_sensor_tag[stream][index]);
}
break;
default:
mgerr("device type(%d) is invalid", group, group, group->device_type);
BUG();
break;
}
p_err:
return ret;
}
int fimc_is_devicemgr_shot_done(struct fimc_is_group *group,
struct fimc_is_frame *ldr_frame,
u32 status)
{
int ret = 0;
unsigned long flags;
/* skip in case of the sensor -> 3AA M2M case */
if (group->device_type == FIMC_IS_DEVICE_ISCHAIN)
return ret;
/* if error happened, cancel the sensor's subdev frames */
if (status) {
mginfo("[F%d] Start CANCEL Other subdev frame\n", group->device, group, ldr_frame->fcount);
flags = fimc_is_group_lock(group, group->device_type, false);
fimc_is_group_subdev_cancel(group, ldr_frame, group->device_type, FS_REQUEST, false);
fimc_is_group_unlock(group, flags, group->device_type, false);
mginfo("[F%d] End CANCEL Other subdev frame\n", group->device, group, ldr_frame->fcount);
}
return ret;
}
#else
int fimc_is_devicemgr_probe(struct fimc_is_devicemgr *devicemgr)
{
return 0;
}
int fimc_is_devicemgr_open(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type)
{
return 0;
}
int fimc_is_devicemgr_binding(struct fimc_is_devicemgr *devicemgr,
struct fimc_is_device_sensor *sensor,
struct fimc_is_device_ischain *ischain,
enum fimc_is_device_type type)
{
return 0;
}
int fimc_is_devicemgr_start(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type)
{
return 0;
}
int fimc_is_devicemgr_stop(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type)
{
return 0;
}
int fimc_is_devicemgr_close(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type)
{
return 0;
}
int fimc_is_devicemgr_shot_callback(struct fimc_is_group *group,
struct fimc_is_frame *frame,
u32 fcount,
enum fimc_is_device_type type)
{
return 0;
}
int fimc_is_devicemgr_shot_done(struct fimc_is_group *group,
struct fimc_is_frame *ldr_frame,
u32 status)
{
return 0;
}
#endif

View file

@ -0,0 +1,95 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DEVICE_MGR_H
#define FIMC_IS_DEVICE_MGR_H
#include "fimc-is-config.h"
#include "fimc-is-device-ischain.h"
#include "fimc-is-device-sensor.h"
#define TAG_DATA_MAX 8
#ifdef CONFIG_USE_SENSOR_GROUP
#define GET_DEVICE_TYPE_BY_GRP(group_id) \
({enum fimc_is_device_type type; \
switch (group_id) { \
case GROUP_ID_SS0: \
case GROUP_ID_SS1: \
case GROUP_ID_SS2: \
case GROUP_ID_SS3: \
case GROUP_ID_SS4: \
case GROUP_ID_SS5: \
type = FIMC_IS_DEVICE_SENSOR; \
break; \
default: \
type = FIMC_IS_DEVICE_ISCHAIN; \
break; \
}; type;})
#define GET_HEAD_GROUP_IN_DEVICE(type, group) \
({ struct fimc_is_group *head; \
head = group->head; \
while (head) { \
if (head->device_type == type) \
break; \
else \
head = head->child; \
}; head;})
#define GET_OUT_FLAG_IN_DEVICE(device_type, out_flag) \
({unsigned long tmp_out_flag; \
if (device_type == FIMC_IS_DEVICE_ISCHAIN) \
tmp_out_flag = ((out_flag) & (~((1 << ENTRY_3AA) - 1))); \
else \
tmp_out_flag = ((out_flag) & ((1 << ENTRY_3AA) - 1)); \
tmp_out_flag;})
#else
#define GET_DEVICE_TYPE_BY_GRP(group_id) FIMC_IS_DEVICE_ISCHAIN
#define GET_HEAD_GROUP_IN_DEVICE(type, group) (group->head)
#define GET_OUT_FLAG_IN_DEVICE(device_type, out_flag) (out_flag)
#endif
struct devicemgr_sensor_tag_data {
struct fimc_is_devicemgr *devicemgr;
struct fimc_is_group *group;
u32 fcount;
u32 stream;
};
struct fimc_is_devicemgr {
struct fimc_is_device_sensor *sensor[FIMC_IS_STREAM_COUNT];
struct fimc_is_device_ischain *ischain[FIMC_IS_STREAM_COUNT];
struct tasklet_struct tasklet_sensor_tag[FIMC_IS_STREAM_COUNT][TAG_DATA_MAX];
struct devicemgr_sensor_tag_data sensor_tag_data[FIMC_IS_STREAM_COUNT][TAG_DATA_MAX];
};
int fimc_is_devicemgr_probe(struct fimc_is_devicemgr *devicemgr);
int fimc_is_devicemgr_open(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type);
int fimc_is_devicemgr_binding(struct fimc_is_devicemgr *devicemgr,
struct fimc_is_device_sensor *sensor,
struct fimc_is_device_ischain *ischain,
enum fimc_is_device_type type);
int fimc_is_devicemgr_start(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type);
int fimc_is_devicemgr_stop(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type);
int fimc_is_devicemgr_close(struct fimc_is_devicemgr *devicemgr,
void *device, enum fimc_is_device_type type);
int fimc_is_devicemgr_shot_callback(struct fimc_is_group *group,
struct fimc_is_frame *frame,
u32 fcount,
enum fimc_is_device_type type);
int fimc_is_devicemgr_shot_done(struct fimc_is_group *group,
struct fimc_is_frame *ldr_frame,
u32 status);
#endif

View file

@ -0,0 +1,807 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is core functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/sched.h>
#include <media/exynos_mc.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_gpio.h>
#endif
#include <exynos-fimc-is-module.h>
#include <exynos-fimc-is-sensor.h>
#include <exynos-fimc-is.h>
#include "fimc-is-config.h"
#include "fimc-is-dt.h"
#include "fimc-is-core.h"
#include "fimc-is-dvfs.h"
#ifdef CONFIG_OF
static int get_pin_lookup_state(struct pinctrl *pinctrl,
struct exynos_sensor_pin (*pin_ctrls)[GPIO_SCENARIO_MAX][GPIO_CTRL_MAX])
{
int ret = 0;
u32 i, j, k;
char pin_name[30];
struct pinctrl_state *s;
for (i = 0; i < SENSOR_SCENARIO_MAX; ++i) {
for (j = 0; j < GPIO_SCENARIO_MAX; ++j) {
for (k = 0; k < GPIO_CTRL_MAX; ++k) {
if (pin_ctrls[i][j][k].act == PIN_FUNCTION) {
snprintf(pin_name, sizeof(pin_name), "%s%d",
pin_ctrls[i][j][k].name,
pin_ctrls[i][j][k].value);
s = pinctrl_lookup_state(pinctrl, pin_name);
if (IS_ERR_OR_NULL(s)) {
err("pinctrl_lookup_state(%s) is failed", pin_name);
ret = -EINVAL;
goto p_err;
}
pin_ctrls[i][j][k].pin = (ulong)s;
}
}
}
}
p_err:
return ret;
}
static int parse_gate_info(struct exynos_platform_fimc_is *pdata, struct device_node *np)
{
int ret = 0;
struct device_node *group_np = NULL;
struct device_node *gate_info_np;
struct property *prop;
struct property *prop2;
const __be32 *p;
const char *s;
u32 i = 0, u = 0;
struct exynos_fimc_is_clk_gate_info *gate_info;
/* get subip of fimc-is info */
gate_info = kzalloc(sizeof(struct exynos_fimc_is_clk_gate_info), GFP_KERNEL);
if (!gate_info) {
printk(KERN_ERR "%s: no memory for fimc_is gate_info\n", __func__);
return -EINVAL;
}
s = NULL;
/* get gate register info */
prop2 = of_find_property(np, "clk_gate_strs", NULL);
of_property_for_each_u32(np, "clk_gate_enums", prop, p, u) {
printk(KERN_INFO "int value: %d\n", u);
s = of_prop_next_string(prop2, s);
if (s != NULL) {
printk(KERN_INFO "String value: %d-%s\n", u, s);
gate_info->gate_str[u] = s;
}
}
/* gate info */
gate_info_np = of_find_node_by_name(np, "clk_gate_ctrl");
if (!gate_info_np) {
printk(KERN_ERR "%s: can't find fimc_is clk_gate_ctrl node\n", __func__);
ret = -ENOENT;
goto p_err;
}
i = 0;
while ((group_np = of_get_next_child(gate_info_np, group_np))) {
struct exynos_fimc_is_clk_gate_group *group =
&gate_info->groups[i];
of_property_for_each_u32(group_np, "mask_clk_on_org", prop, p, u) {
printk(KERN_INFO "(%d) int1 value: %d\n", i, u);
group->mask_clk_on_org |= (1 << u);
}
of_property_for_each_u32(group_np, "mask_clk_off_self_org", prop, p, u) {
printk(KERN_INFO "(%d) int2 value: %d\n", i, u);
group->mask_clk_off_self_org |= (1 << u);
}
of_property_for_each_u32(group_np, "mask_clk_off_depend", prop, p, u) {
printk(KERN_INFO "(%d) int3 value: %d\n", i, u);
group->mask_clk_off_depend |= (1 << u);
}
of_property_for_each_u32(group_np, "mask_cond_for_depend", prop, p, u) {
printk(KERN_INFO "(%d) int4 value: %d\n", i, u);
group->mask_cond_for_depend |= (1 << u);
}
i++;
printk(KERN_INFO "(%d) [0x%x , 0x%x, 0x%x, 0x%x\n", i,
group->mask_clk_on_org,
group->mask_clk_off_self_org,
group->mask_clk_off_depend,
group->mask_cond_for_depend
);
}
pdata->gate_info = gate_info;
pdata->gate_info->clk_on_off = exynos_fimc_is_clk_gate;
return 0;
p_err:
kfree(gate_info);
return ret;
}
#if defined(CONFIG_PM_DEVFREQ)
DECLARE_EXTERN_DVFS_DT(FIMC_IS_SN_END);
static int parse_dvfs_data(struct exynos_platform_fimc_is *pdata, struct device_node *np, int index)
{
int i;
u32 temp;
char *pprop;
char buf[64];
for (i = 0; i < FIMC_IS_SN_END; i++) {
sprintf(buf, "%s%s", fimc_is_dvfs_dt_arr[i].parse_scenario_nm, "int");
DT_READ_U32(np, buf, pdata->dvfs_data[index][fimc_is_dvfs_dt_arr[i].scenario_id][FIMC_IS_DVFS_INT]);
sprintf(buf, "%s%s", fimc_is_dvfs_dt_arr[i].parse_scenario_nm, "cam");
DT_READ_U32(np, buf, pdata->dvfs_data[index][fimc_is_dvfs_dt_arr[i].scenario_id][FIMC_IS_DVFS_CAM]);
sprintf(buf, "%s%s", fimc_is_dvfs_dt_arr[i].parse_scenario_nm, "mif");
DT_READ_U32(np, buf, pdata->dvfs_data[index][fimc_is_dvfs_dt_arr[i].scenario_id][FIMC_IS_DVFS_MIF]);
sprintf(buf, "%s%s", fimc_is_dvfs_dt_arr[i].parse_scenario_nm, "i2c");
DT_READ_U32(np, buf, pdata->dvfs_data[index][fimc_is_dvfs_dt_arr[i].scenario_id][FIMC_IS_DVFS_I2C]);
sprintf(buf, "%s%s", fimc_is_dvfs_dt_arr[i].parse_scenario_nm, "hpg");
DT_READ_U32(np, buf, pdata->dvfs_data[index][fimc_is_dvfs_dt_arr[i].scenario_id][FIMC_IS_DVFS_HPG]);
}
#ifdef DBG_DUMP_DVFS_DT
for (i = 0; i < FIMC_IS_SN_END; i++) {
probe_info("---- %s ----", fimc_is_dvfs_dt_arr[i].parse_scenario_nm);
probe_info("[%d][%d][INT] = %d", index, i, pdata->dvfs_data[index][i][FIMC_IS_DVFS_INT]);
probe_info("[%d][%d][CAM] = %d", index, i, pdata->dvfs_data[index][i][FIMC_IS_DVFS_CAM]);
probe_info("[%d][%d][MIF] = %d", index, i, pdata->dvfs_data[index][i][FIMC_IS_DVFS_MIF]);
probe_info("[%d][%d][I2C] = %d", index, i, pdata->dvfs_data[index][i][FIMC_IS_DVFS_I2C]);
probe_info("[%d][%d][HPG] = %d", index, i, pdata->dvfs_data[index][i][FIMC_IS_DVFS_HPG]);
}
#endif
return 0;
}
#else
static int parse_dvfs_data(struct exynos_platform_fimc_is *pdata, struct device_node *np, int index)
{
return 0;
}
#endif
static int parse_dvfs_table(struct fimc_is_dvfs_ctrl *dvfs,
struct exynos_platform_fimc_is *pdata, struct device_node *np)
{
int ret = 0;
u32 table_cnt;
struct device_node *table_np;
const char *dvfs_table_desc;
table_np = NULL;
table_cnt = 0;
while ((table_np = of_get_next_child(np, table_np)) &&
(table_cnt < FIMC_IS_DVFS_TABLE_IDX_MAX)) {
ret = of_property_read_string(table_np, "desc", &dvfs_table_desc);
if (ret)
dvfs_table_desc = "NOT defined";
probe_info("dvfs table[%d] is %s", table_cnt, dvfs_table_desc);
parse_dvfs_data(pdata, table_np, table_cnt);
table_cnt++;
}
dvfs->dvfs_table_max = table_cnt;
return ret;
}
int fimc_is_parse_dt(struct platform_device *pdev)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_dvfs_ctrl *dvfs;
struct exynos_platform_fimc_is *pdata;
struct device *dev;
struct device_node *dvfs_np = NULL;
struct device_node *vender_np = NULL;
struct device_node *np;
BUG_ON(!pdev);
dev = &pdev->dev;
np = dev->of_node;
core = dev_get_drvdata(&pdev->dev);
if (!core) {
probe_err("core is NULL");
return -ENOMEM;
}
pdata = kzalloc(sizeof(struct exynos_platform_fimc_is), GFP_KERNEL);
if (!pdata) {
probe_err("no memory for platform data");
return -ENOMEM;
}
dvfs = &core->resourcemgr.dvfs_ctrl;
pdata->clk_get = exynos_fimc_is_clk_get;
pdata->clk_cfg = exynos_fimc_is_clk_cfg;
pdata->clk_on = exynos_fimc_is_clk_on;
pdata->clk_off = exynos_fimc_is_clk_off;
pdata->print_clk = exynos_fimc_is_print_clk;
if (parse_gate_info(pdata, np) < 0)
probe_err("can't parse clock gate info node");
vender_np = of_find_node_by_name(np, "vender");
if (vender_np) {
ret = fimc_is_vender_dt(vender_np);
if (ret)
probe_err("fimc_is_vender_dt is fail(%d)", ret);
}
pdata->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(pdata->pinctrl)) {
probe_err("devm_pinctrl_get is fail");
goto p_err;
}
dvfs_np = of_find_node_by_name(np, "fimc_is_dvfs");
if (dvfs_np) {
ret = parse_dvfs_table(dvfs, pdata, dvfs_np);
if (ret)
probe_err("parse_dvfs_table is fail(%d)", ret);
}
dev->platform_data = pdata;
return 0;
p_err:
kfree(pdata);
return ret;
}
int fimc_is_sensor_parse_dt(struct platform_device *pdev)
{
int ret = 0;
struct exynos_platform_fimc_is_sensor *pdata;
struct device_node *dnode;
struct device *dev;
BUG_ON(!pdev);
BUG_ON(!pdev->dev.of_node);
dev = &pdev->dev;
dnode = dev->of_node;
pdata = kzalloc(sizeof(struct exynos_platform_fimc_is_sensor), GFP_KERNEL);
if (!pdata) {
err("%s: no memory for platform data", __func__);
return -ENOMEM;
}
pdata->iclk_cfg = exynos_fimc_is_sensor_iclk_cfg;
pdata->iclk_on = exynos_fimc_is_sensor_iclk_on;
pdata->iclk_off = exynos_fimc_is_sensor_iclk_off;
pdata->mclk_on = exynos_fimc_is_sensor_mclk_on;
pdata->mclk_off = exynos_fimc_is_sensor_mclk_off;
ret = of_property_read_u32(dnode, "id", &pdata->id);
if (ret) {
err("id read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "scenario", &pdata->scenario);
if (ret) {
err("scenario read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "csi_ch", &pdata->csi_ch);
if (ret) {
err("csi_ch read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "flite_ch", &pdata->flite_ch);
if (ret) {
err("flite_ch read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "is_bns", &pdata->is_bns);
if (ret) {
err("is_bns read is fail(%d)", ret);
goto p_err;
}
pdev->id = pdata->id;
dev->platform_data = pdata;
return ret;
p_err:
kfree(pdata);
return ret;
}
int fimc_is_preprocessor_parse_dt(struct platform_device *pdev)
{
int ret = 0;
struct exynos_platform_fimc_is_preproc *pdata;
struct device_node *dnode;
struct device *dev;
BUG_ON(!pdev);
BUG_ON(!pdev->dev.of_node);
dev = &pdev->dev;
dnode = dev->of_node;
pdata = kzalloc(sizeof(struct exynos_platform_fimc_is_preproc), GFP_KERNEL);
if (!pdata) {
probe_err("%s: no memory for platform data\n", __func__);
return -ENOMEM;
}
ret = of_property_read_u32(dnode, "scenario", &pdata->scenario);
if (ret) {
probe_err("scenario read is fail(%d)", ret);
ret = -EINVAL;
goto p_err;
}
ret = of_property_read_u32(dnode, "mclk_ch", &pdata->mclk_ch);
if (ret) {
probe_err("mclk_ch read is fail(%d)", ret);
ret = -EINVAL;
goto p_err;
}
ret = of_property_read_u32(dnode, "id", &pdata->id);
if (ret) {
probe_err("csi_ch read is fail(%d)", ret);
goto p_err;
}
pdata->iclk_cfg = exynos_fimc_is_preproc_iclk_cfg;
pdata->iclk_on = exynos_fimc_is_preproc_iclk_on;
pdata->iclk_off = exynos_fimc_is_preproc_iclk_off;
pdata->mclk_on = exynos_fimc_is_preproc_mclk_on;
pdata->mclk_off = exynos_fimc_is_preproc_mclk_off;
dev->platform_data = pdata;
return ret;
p_err:
kfree(pdata);
return ret;
}
static int parse_af_data(struct exynos_platform_fimc_is_module *pdata, struct device_node *dnode)
{
u32 temp;
char *pprop;
DT_READ_U32(dnode, "product_name", pdata->af_product_name);
DT_READ_U32(dnode, "i2c_addr", pdata->af_i2c_addr);
DT_READ_U32(dnode, "i2c_ch", pdata->af_i2c_ch);
return 0;
}
static int parse_flash_data(struct exynos_platform_fimc_is_module *pdata, struct device_node *dnode)
{
u32 temp;
char *pprop;
DT_READ_U32(dnode, "product_name", pdata->flash_product_name);
DT_READ_U32(dnode, "flash_first_gpio", pdata->flash_first_gpio);
DT_READ_U32(dnode, "flash_second_gpio", pdata->flash_second_gpio);
return 0;
}
static int parse_preprocessor_data(struct exynos_platform_fimc_is_module *pdata, struct device_node *dnode)
{
u32 temp;
char *pprop;
DT_READ_U32(dnode, "product_name", pdata->preprocessor_product_name);
DT_READ_U32(dnode, "spi_channel", pdata->preprocessor_spi_channel);
DT_READ_U32(dnode, "i2c_addr", pdata->preprocessor_i2c_addr);
DT_READ_U32(dnode, "i2c_ch", pdata->preprocessor_i2c_ch);
DT_READ_U32_DEFAULT(dnode, "dma_ch", pdata->preprocessor_dma_channel, DMA_CH_NOT_DEFINED);
return 0;
}
static int parse_ois_data(struct exynos_platform_fimc_is_module *pdata, struct device_node *dnode)
{
u32 temp;
char *pprop;
DT_READ_U32(dnode, "product_name", pdata->ois_product_name);
DT_READ_U32(dnode, "i2c_addr", pdata->ois_i2c_addr);
DT_READ_U32(dnode, "i2c_ch", pdata->ois_i2c_ch);
return 0;
}
static int parse_power_seq_data(struct exynos_platform_fimc_is_module *pdata, struct device_node *dnode)
{
u32 temp;
char *pprop;
char *name;
struct device_node *sn_np, *seq_np;
for_each_child_of_node(dnode, sn_np) {
u32 sensor_scenario, gpio_scenario;
DT_READ_U32(sn_np, "sensor_scenario", sensor_scenario);
DT_READ_U32(sn_np, "gpio_scenario",gpio_scenario);
pr_info("power_seq[%s] : sensor_scenario=%d, gpio_scenario=%d\n",
sn_np->name, sensor_scenario, gpio_scenario);
SET_PIN_INIT(pdata, sensor_scenario, gpio_scenario);
for_each_child_of_node(sn_np, seq_np) {
struct exynos_sensor_pin sensor_pin;
char* pin_name;
memset(&sensor_pin, 0, sizeof(struct exynos_sensor_pin));
DT_READ_STR(seq_np, "pin", pin_name);
if(!strcmp(pin_name, "gpio_none"))
sensor_pin.pin = 0;
else
sensor_pin.pin = of_get_named_gpio(dnode->parent, pin_name, 0);
DT_READ_STR(seq_np, "pname", sensor_pin.name);
if(sensor_pin.name[0] == '\0')
sensor_pin.name = NULL;
DT_READ_U32(seq_np, "act", sensor_pin.act);
DT_READ_U32(seq_np, "value", sensor_pin.value);
DT_READ_U32(seq_np, "delay", sensor_pin.delay);
DT_READ_U32(seq_np, "voltage", sensor_pin.voltage);
pr_debug("power_seq node_name=%s\n", seq_np->full_name);
pr_info("power_seq SET_PIN: pin_name=%s, name=%s, act=%d, value=%d, delay=%d, voltage=%d\n",
pin_name, sensor_pin.name, sensor_pin.act, sensor_pin.value, sensor_pin.delay, sensor_pin.voltage);
SET_PIN_VOLTAGE(pdata, sensor_scenario, gpio_scenario, sensor_pin.pin, sensor_pin.name,
sensor_pin.act, sensor_pin.value, sensor_pin.delay, sensor_pin.voltage);
}
}
return 0;
}
/* Deprecated. Use fimc_is_module_parse_dt */
int fimc_is_sensor_module_parse_dt(struct platform_device *pdev,
fimc_is_moudle_dt_callback module_callback)
{
int ret = 0;
struct exynos_platform_fimc_is_module *pdata;
struct device_node *dnode;
struct device_node *af_np;
struct device_node *flash_np;
struct device_node *preprocessor_np;
struct device_node *ois_np;
struct device_node *power_np;
struct device *dev;
BUG_ON(!pdev);
BUG_ON(!pdev->dev.of_node);
BUG_ON(!module_callback);
dev = &pdev->dev;
dnode = dev->of_node;
pdata = kzalloc(sizeof(struct exynos_platform_fimc_is_module), GFP_KERNEL);
if (!pdata) {
probe_err("%s: no memory for platform data", __func__);
return -ENOMEM;
}
pdata->gpio_cfg = exynos_fimc_is_module_pins_cfg;
pdata->gpio_dbg = exynos_fimc_is_module_pins_dbg;
ret = of_property_read_u32(dnode, "id", &pdata->id);
if (ret) {
probe_err("id read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "mclk_ch", &pdata->mclk_ch);
if (ret) {
probe_err("mclk_ch read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "sensor_i2c_ch", &pdata->sensor_i2c_ch);
if (ret) {
probe_err("i2c_ch read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "sensor_i2c_addr", &pdata->sensor_i2c_addr);
if (ret) {
probe_err("i2c_addr read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "position", &pdata->position);
if (ret) {
probe_err("id read is fail(%d)", ret);
goto p_err;
}
af_np = of_find_node_by_name(dnode, "af");
if (!af_np) {
pdata->af_product_name = ACTUATOR_NAME_NOTHING;
} else {
parse_af_data(pdata, af_np);
}
flash_np = of_find_node_by_name(dnode, "flash");
if (!flash_np) {
pdata->flash_product_name = FLADRV_NAME_NOTHING;
} else {
parse_flash_data(pdata, flash_np);
}
preprocessor_np = of_find_node_by_name(dnode, "preprocessor");
if (!preprocessor_np) {
pdata->preprocessor_product_name = PREPROCESSOR_NAME_NOTHING;
} else {
parse_preprocessor_data(pdata, preprocessor_np);
}
ois_np = of_find_node_by_name(dnode, "ois");
if (!ois_np) {
pdata->ois_product_name = OIS_NAME_NOTHING;
} else {
parse_ois_data(pdata, ois_np);
}
pdata->power_seq_dt = of_property_read_bool(dnode, "use_power_seq");
if(pdata->power_seq_dt == true) {
power_np = of_find_node_by_name(dnode, "power_seq");
if (!power_np) {
probe_err("power sequence is not declared to DT");
goto p_err;
} else {
parse_power_seq_data(pdata, power_np);
}
} else {
ret = module_callback(pdev, pdata);
if (ret) {
probe_err("sensor dt callback is fail(%d)", ret);
goto p_err;
}
}
pdata->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(pdata->pinctrl)) {
probe_err("devm_pinctrl_get is fail");
goto p_err;
}
ret = get_pin_lookup_state(pdata->pinctrl, pdata->pin_ctrls);
if (ret) {
probe_err("get_pin_lookup_state is fail(%d)", ret);
goto p_err;
}
dev->platform_data = pdata;
return ret;
p_err:
kfree(pdata);
return ret;
}
/* New function for module parse dt. Use this instead of fimc_is_sensor_module_parse_dt */
int fimc_is_module_parse_dt(struct device *dev,
fimc_is_moudle_callback module_callback)
{
int ret = 0;
struct exynos_platform_fimc_is_module *pdata;
struct device_node *dnode;
struct device_node *af_np;
struct device_node *flash_np;
struct device_node *preprocessor_np;
struct device_node *ois_np;
BUG_ON(!dev);
BUG_ON(!dev->of_node);
BUG_ON(!module_callback);
dnode = dev->of_node;
pdata = kzalloc(sizeof(struct exynos_platform_fimc_is_module), GFP_KERNEL);
if (!pdata) {
probe_err("%s: no memory for platform data", __func__);
return -ENOMEM;
}
pdata->gpio_cfg = exynos_fimc_is_module_pins_cfg;
pdata->gpio_dbg = exynos_fimc_is_module_pins_dbg;
ret = of_property_read_u32(dnode, "id", &pdata->id);
if (ret) {
probe_err("id read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "mclk_ch", &pdata->mclk_ch);
if (ret) {
probe_err("mclk_ch read is fail(%d)", ret);
goto p_err;
}
ret = of_property_read_u32(dnode, "sensor_i2c_ch", &pdata->sensor_i2c_ch);
if (ret) {
probe_err("i2c_ch read is fail(%d)", ret);
}
ret = of_property_read_u32(dnode, "sensor_i2c_addr", &pdata->sensor_i2c_addr);
if (ret) {
probe_err("i2c_addr read is fail(%d)", ret);
}
ret = of_property_read_u32(dnode, "position", &pdata->position);
if (ret) {
probe_err("id read is fail(%d)", ret);
goto p_err;
}
af_np = of_find_node_by_name(dnode, "af");
if (!af_np) {
pdata->af_product_name = ACTUATOR_NAME_NOTHING;
} else {
parse_af_data(pdata, af_np);
}
flash_np = of_find_node_by_name(dnode, "flash");
if (!flash_np) {
pdata->flash_product_name = FLADRV_NAME_NOTHING;
} else {
parse_flash_data(pdata, flash_np);
}
preprocessor_np = of_find_node_by_name(dnode, "preprocessor");
if (!preprocessor_np) {
pdata->preprocessor_product_name = PREPROCESSOR_NAME_NOTHING;
} else {
parse_preprocessor_data(pdata, preprocessor_np);
}
ois_np = of_find_node_by_name(dnode, "ois");
if (!ois_np) {
pdata->ois_product_name = OIS_NAME_NOTHING;
} else {
parse_ois_data(pdata, ois_np);
}
ret = module_callback(dev, pdata);
if (ret) {
probe_err("sensor dt callback is fail(%d)", ret);
goto p_err;
}
pdata->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(pdata->pinctrl)) {
probe_err("devm_pinctrl_get is fail");
goto p_err;
}
ret = get_pin_lookup_state(pdata->pinctrl, pdata->pin_ctrls);
if (ret) {
probe_err("get_pin_lookup_state is fail(%d)", ret);
goto p_err;
}
dev->platform_data = pdata;
return ret;
p_err:
kfree(pdata);
return ret;
}
int fimc_is_spi_parse_dt(struct fimc_is_spi *spi)
{
int ret = 0;
struct device_node *np;
struct device *dev;
struct pinctrl_state *s;
BUG_ON(!spi);
dev = &spi->device->dev;
np = of_find_compatible_node(NULL,NULL, spi->node);
if(np == NULL) {
probe_err("compatible: fail to read, spi_parse_dt");
ret = -ENODEV;
goto p_err;
}
spi->use_spi_pinctrl = of_property_read_bool(np, "use_spi_pinctrl");
if (!spi->use_spi_pinctrl) {
probe_info("%s: spi dt parsing skipped\n", __func__);
goto p_err;
}
spi->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(spi->pinctrl)) {
probe_err("devm_pinctrl_get is fail");
goto p_err;
}
s = pinctrl_lookup_state(spi->pinctrl, "ssn_out");
if (IS_ERR_OR_NULL(s)) {
err("pinctrl_lookup_state(%s) is failed", "ssn_out");
ret = -EINVAL;
goto p_err;
}
spi->pin_ssn_out = s;
s = pinctrl_lookup_state(spi->pinctrl, "ssn_fn");
if (IS_ERR_OR_NULL(s)) {
err("pinctrl_lookup_state(%s) is failed", "ssn_fn");
ret = -EINVAL;
goto p_err;
}
spi->pin_ssn_fn = s;
spi->parent_pinctrl = devm_pinctrl_get(spi->device->dev.parent->parent);
s = pinctrl_lookup_state(spi->parent_pinctrl, "spi_out");
if (IS_ERR_OR_NULL(s)) {
err("pinctrl_lookup_state(%s) is failed", "spi_out");
ret = -EINVAL;
goto p_err;
}
spi->parent_pin_out = s;
s = pinctrl_lookup_state(spi->parent_pinctrl, "spi_fn");
if (IS_ERR_OR_NULL(s)) {
err("pinctrl_lookup_state(%s) is failed", "spi_fn");
ret = -EINVAL;
goto p_err;
}
spi->parent_pin_fn = s;
p_err:
return ret;
}
#else
struct exynos_platform_fimc_is *fimc_is_parse_dt(struct device *dev)
{
return ERR_PTR(-EINVAL);
}
#endif

View file

@ -0,0 +1,64 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DT_H
#define FIMC_IS_DT_H
#include "fimc-is-spi.h"
#include <exynos-fimc-is-module.h>
#include <exynos-fimc-is-sensor.h>
#define DT_READ_U32(node, key, value) do {\
pprop = key; \
temp = 0; \
if (of_property_read_u32((node), key, &temp)) \
pr_warn("%s: no property in the node.\n", pprop);\
(value) = temp; \
} while (0)
#define DT_READ_U32_DEFAULT(node, key, value, default_value) do {\
pprop = key; \
temp = 0; \
if (of_property_read_u32((node), key, &temp)) {\
pr_warn("%s: no property in the node.\n", pprop);\
(value) = default_value;\
} else {\
(value) = temp; \
}\
} while (0)
#define DT_READ_STR(node, key, value) do {\
pprop = key; \
if (of_property_read_string((node), key, (const char **)&name)) \
pr_warn("%s: no property in the node.\n", pprop);\
(value) = name; \
} while (0)
/* Deprecated. Use fimc_is_moudle_callback */
typedef int (*fimc_is_moudle_dt_callback)(struct platform_device *pdev,
struct exynos_platform_fimc_is_module *pdata);
/* New function for module callback. Use this instead of fimc_is_moudle_dt_callback */
typedef int (*fimc_is_moudle_callback)(struct device *dev,
struct exynos_platform_fimc_is_module *pdata);
int fimc_is_parse_dt(struct platform_device *pdev);
int fimc_is_sensor_parse_dt(struct platform_device *pdev);
int fimc_is_preprocessor_parse_dt(struct platform_device *pdev);
/* Deprecated. Use fimc_is_module_parse_dt */
int fimc_is_sensor_module_parse_dt(struct platform_device *pdev,
fimc_is_moudle_dt_callback callback);
/* New function for module parse dt. Use this instead of fimc_is_sensor_module_parse_dt */
int fimc_is_module_parse_dt(struct device *dev,
fimc_is_moudle_callback callback);
int fimc_is_spi_parse_dt(struct fimc_is_spi *spi);
int fimc_is_power_setpin(struct device *dev, int position, int sensor_id);
#endif

View file

@ -0,0 +1,475 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is core functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/slab.h>
#include "fimc-is-core.h"
#include "fimc-is-dvfs.h"
#include "fimc-is-hw-dvfs.h"
#include <linux/videodev2_exynos_camera.h>
#ifdef CONFIG_PM_DEVFREQ
extern struct pm_qos_request exynos_isp_qos_int;
extern struct pm_qos_request exynos_isp_qos_mem;
extern struct pm_qos_request exynos_isp_qos_cam;
extern struct pm_qos_request exynos_isp_qos_hpg;
static inline int fimc_is_get_start_sensor_cnt(struct fimc_is_core *core)
{
int i, sensor_cnt = 0;
for (i = 0; i < FIMC_IS_SENSOR_COUNT; i++)
if (test_bit(FIMC_IS_SENSOR_FRONT_START, &(core->sensor[i].state)))
sensor_cnt++;
return sensor_cnt;
}
static int fimc_is_get_target_resol(struct fimc_is_device_ischain *device)
{
int resol = 0;
#ifdef SOC_MCS
int i = 0;
struct fimc_is_group *group;
group = &device->group_mcs;
if (!test_bit(FIMC_IS_GROUP_INIT, &group->state))
return resol;
for (i = ENTRY_M0P; i <= ENTRY_M4P; i++)
if (group->subdev[i] && test_bit(FIMC_IS_SUBDEV_START, &(group->subdev[i]->state)))
resol = max_t(int, resol, group->subdev[i]->output.width * group->subdev[i]->output.height);
#else
resol = device->scp.output.width * device->scp.output.height;
#endif
return resol;
}
int fimc_is_dvfs_init(struct fimc_is_resourcemgr *resourcemgr)
{
int ret = 0;
struct fimc_is_dvfs_ctrl *dvfs_ctrl;
BUG_ON(!resourcemgr);
dvfs_ctrl = &resourcemgr->dvfs_ctrl;
dvfs_ctrl->cur_int_qos = 0;
dvfs_ctrl->cur_mif_qos = 0;
dvfs_ctrl->cur_cam_qos = 0;
dvfs_ctrl->cur_i2c_qos = 0;
dvfs_ctrl->cur_disp_qos = 0;
dvfs_ctrl->cur_hpg_qos = 0;
dvfs_ctrl->cur_hmp_bst = 0;
/* init spin_lock for clock gating */
mutex_init(&dvfs_ctrl->lock);
if (!(dvfs_ctrl->static_ctrl))
dvfs_ctrl->static_ctrl =
kzalloc(sizeof(struct fimc_is_dvfs_scenario_ctrl), GFP_KERNEL);
if (!(dvfs_ctrl->dynamic_ctrl))
dvfs_ctrl->dynamic_ctrl =
kzalloc(sizeof(struct fimc_is_dvfs_scenario_ctrl), GFP_KERNEL);
if (!(dvfs_ctrl->external_ctrl))
dvfs_ctrl->external_ctrl =
kzalloc(sizeof(struct fimc_is_dvfs_scenario_ctrl), GFP_KERNEL);
if (!dvfs_ctrl->static_ctrl || !dvfs_ctrl->dynamic_ctrl || !dvfs_ctrl->external_ctrl) {
err("dvfs_ctrl alloc is failed!!\n");
return -ENOMEM;
}
/* assign static / dynamic scenario check logic data */
ret = fimc_is_hw_dvfs_init((void *)dvfs_ctrl);
if (ret) {
err("fimc_is_hw_dvfs_init is failed(%d)\n", ret);
return -EINVAL;
}
/* default value is 0 */
dvfs_ctrl->dvfs_table_idx = 0;
clear_bit(FIMC_IS_DVFS_SEL_TABLE, &dvfs_ctrl->state);
return 0;
}
int fimc_is_dvfs_sel_table(struct fimc_is_resourcemgr *resourcemgr)
{
int ret = 0;
struct fimc_is_dvfs_ctrl *dvfs_ctrl;
u32 dvfs_table_idx = 0;
BUG_ON(!resourcemgr);
dvfs_ctrl = &resourcemgr->dvfs_ctrl;
if (test_bit(FIMC_IS_DVFS_SEL_TABLE, &dvfs_ctrl->state))
return 0;
switch(resourcemgr->hal_version) {
case IS_HAL_VER_1_0:
dvfs_table_idx = 0;
break;
case IS_HAL_VER_3_2:
dvfs_table_idx = 1;
break;
default:
err("hal version is unknown");
dvfs_table_idx = 0;
ret = -EINVAL;
break;
}
if (dvfs_table_idx >= dvfs_ctrl->dvfs_table_max) {
err("dvfs index(%d) is invalid", dvfs_table_idx);
ret = -EINVAL;
goto p_err;
}
resourcemgr->dvfs_ctrl.dvfs_table_idx = dvfs_table_idx;
set_bit(FIMC_IS_DVFS_SEL_TABLE, &dvfs_ctrl->state);
p_err:
info("[RSC] %s(%d):%d\n", __func__, dvfs_table_idx, ret);
return ret;
}
int fimc_is_dvfs_sel_static(struct fimc_is_device_ischain *device)
{
struct fimc_is_core *core;
struct fimc_is_dvfs_ctrl *dvfs_ctrl;
struct fimc_is_dvfs_scenario_ctrl *static_ctrl;
struct fimc_is_dvfs_scenario *scenarios;
struct fimc_is_resourcemgr *resourcemgr;
int i, scenario_id, scenario_cnt;
int position, resol, fps, stream_cnt;
BUG_ON(!device);
BUG_ON(!device->interface);
core = (struct fimc_is_core *)device->interface->core;
resourcemgr = device->resourcemgr;
dvfs_ctrl = &(resourcemgr->dvfs_ctrl);
static_ctrl = dvfs_ctrl->static_ctrl;
if (!test_bit(FIMC_IS_DVFS_SEL_TABLE, &dvfs_ctrl->state)) {
err("dvfs table is NOT selected");
return -EINVAL;
}
/* static scenario */
if (!static_ctrl) {
err("static_dvfs_ctrl is NULL");
return -EINVAL;
}
if (static_ctrl->scenario_cnt == 0) {
pr_debug("static_scenario's count is zero");
return -EINVAL;
}
scenarios = static_ctrl->scenarios;
scenario_cnt = static_ctrl->scenario_cnt;
position = fimc_is_sensor_g_position(device->sensor);
resol = fimc_is_get_target_resol(device);
fps = fimc_is_sensor_g_framerate(device->sensor);
stream_cnt = fimc_is_get_start_sensor_cnt(core);
for (i = 0; i < scenario_cnt; i++) {
if (!scenarios[i].check_func) {
warn("check_func[%d] is NULL\n", i);
continue;
}
if ((scenarios[i].check_func(device, NULL, position, resol, fps, stream_cnt)) > 0) {
scenario_id = scenarios[i].scenario_id;
static_ctrl->cur_scenario_id = scenario_id;
static_ctrl->cur_scenario_idx = i;
static_ctrl->cur_frame_tick = scenarios[i].keep_frame_tick;
return scenario_id;
}
}
warn("couldn't find static dvfs scenario [sensor:(%d/%d)/fps:%d/setfile:%d/resol:(%d)]\n",
fimc_is_get_start_sensor_cnt(core),
device->sensor->pdev->id,
fps, (device->setfile & FIMC_IS_SETFILE_MASK), resol);
static_ctrl->cur_scenario_id = FIMC_IS_SN_DEFAULT;
static_ctrl->cur_scenario_idx = -1;
static_ctrl->cur_frame_tick = -1;
return FIMC_IS_SN_DEFAULT;
}
int fimc_is_dvfs_sel_dynamic(struct fimc_is_device_ischain *device, struct fimc_is_group *group)
{
int ret;
struct fimc_is_dvfs_ctrl *dvfs_ctrl;
struct fimc_is_dvfs_scenario_ctrl *dynamic_ctrl;
struct fimc_is_dvfs_scenario *scenarios;
struct fimc_is_resourcemgr *resourcemgr;
int i, scenario_id, scenario_cnt;
int position, resol, fps;
BUG_ON(!device);
resourcemgr = device->resourcemgr;
dvfs_ctrl = &(resourcemgr->dvfs_ctrl);
dynamic_ctrl = dvfs_ctrl->dynamic_ctrl;
if (!test_bit(FIMC_IS_DVFS_SEL_TABLE, &dvfs_ctrl->state)) {
err("dvfs table is NOT selected");
return -EINVAL;
}
/* dynamic scenario */
if (!dynamic_ctrl) {
err("dynamic_dvfs_ctrl is NULL");
return -EINVAL;
}
if (dynamic_ctrl->scenario_cnt == 0) {
pr_debug("dynamic_scenario's count is zero");
return -EINVAL;
}
scenarios = dynamic_ctrl->scenarios;
scenario_cnt = dynamic_ctrl->scenario_cnt;
if (dynamic_ctrl->cur_frame_tick >= 0) {
(dynamic_ctrl->cur_frame_tick)--;
/*
* when cur_frame_tick is lower than 0, clear current scenario.
* This means that current frame tick to keep dynamic scenario
* was expired.
*/
if (dynamic_ctrl->cur_frame_tick < 0) {
dynamic_ctrl->cur_scenario_id = -1;
dynamic_ctrl->cur_scenario_idx = -1;
}
}
if (!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state))
goto p_again;
position = fimc_is_sensor_g_position(device->sensor);
resol = fimc_is_get_target_resol(device);
fps = fimc_is_sensor_g_framerate(device->sensor);
for (i = 0; i < scenario_cnt; i++) {
if (!scenarios[i].check_func) {
warn("check_func[%d] is NULL\n", i);
continue;
}
ret = scenarios[i].check_func(device, group, position, resol, fps, 0);
switch (ret) {
case DVFS_MATCHED:
scenario_id = scenarios[i].scenario_id;
dynamic_ctrl->cur_scenario_id = scenario_id;
dynamic_ctrl->cur_scenario_idx = i;
dynamic_ctrl->cur_frame_tick = scenarios[i].keep_frame_tick;
return scenario_id;
case DVFS_SKIP:
goto p_again;
case DVFS_NOT_MATCHED:
default:
continue;
}
}
p_again:
return -EAGAIN;
}
int fimc_is_dvfs_sel_external(struct fimc_is_device_sensor *device)
{
struct fimc_is_core *core;
struct fimc_is_dvfs_ctrl *dvfs_ctrl;
struct fimc_is_dvfs_scenario_ctrl *external_ctrl;
struct fimc_is_dvfs_scenario *scenarios;
struct fimc_is_resourcemgr *resourcemgr;
int i, scenario_id, scenario_cnt;
int position, resol, fps, stream_cnt;
BUG_ON(!device);
core = device->private_data;
resourcemgr = device->resourcemgr;
dvfs_ctrl = &(resourcemgr->dvfs_ctrl);
external_ctrl = dvfs_ctrl->external_ctrl;
if (!test_bit(FIMC_IS_DVFS_SEL_TABLE, &dvfs_ctrl->state)) {
err("dvfs table is NOT selected");
return -EINVAL;
}
/* external scenario */
if (!external_ctrl) {
warn("external_dvfs_ctrl is NULL, default max dvfs lv");
return FIMC_IS_SN_MAX;
}
scenarios = external_ctrl->scenarios;
scenario_cnt = external_ctrl->scenario_cnt;
position = fimc_is_sensor_g_position(device);
resol = fimc_is_sensor_g_width(device) * fimc_is_sensor_g_height(device);
fps = fimc_is_sensor_g_framerate(device);
stream_cnt = fimc_is_get_start_sensor_cnt(core);
for (i = 0; i < scenario_cnt; i++) {
if (!scenarios[i].ext_check_func) {
warn("check_func[%d] is NULL\n", i);
continue;
}
if ((scenarios[i].ext_check_func(device, position, resol, fps, stream_cnt)) > 0) {
scenario_id = scenarios[i].scenario_id;
external_ctrl->cur_scenario_id = scenario_id;
external_ctrl->cur_scenario_idx = i;
external_ctrl->cur_frame_tick = scenarios[i].keep_frame_tick;
return scenario_id;
}
}
warn("couldn't find external dvfs scenario [sensor:(%d/%d)/fps:%d/resol:(%d)]\n",
stream_cnt, position, fps, resol);
external_ctrl->cur_scenario_id = FIMC_IS_SN_MAX;
external_ctrl->cur_scenario_idx = -1;
external_ctrl->cur_frame_tick = -1;
return FIMC_IS_SN_MAX;
}
int fimc_is_get_qos(struct fimc_is_core *core, u32 type, u32 scenario_id)
{
struct exynos_platform_fimc_is *pdata = NULL;
int qos = 0;
u32 dvfs_idx = core->resourcemgr.dvfs_ctrl.dvfs_table_idx;
pdata = core->pdata;
if (pdata == NULL) {
err("pdata is NULL\n");
return -EINVAL;
}
if (type >= FIMC_IS_DVFS_END) {
err("Cannot find DVFS value");
return -EINVAL;
}
if (dvfs_idx >= FIMC_IS_DVFS_TABLE_IDX_MAX) {
err("invalid dvfs index(%d)", dvfs_idx);
dvfs_idx = 0;
}
qos = pdata->dvfs_data[dvfs_idx][scenario_id][type];
return qos;
}
int fimc_is_set_dvfs(struct fimc_is_core *core, struct fimc_is_device_ischain *device, u32 scenario_id)
{
int ret = 0;
int int_qos, mif_qos, i2c_qos, cam_qos, disp_qos, hpg_qos;
struct fimc_is_resourcemgr *resourcemgr;
struct fimc_is_dvfs_ctrl *dvfs_ctrl;
if (core == NULL) {
err("core is NULL\n");
return -EINVAL;
}
resourcemgr = &core->resourcemgr;
dvfs_ctrl = &(resourcemgr->dvfs_ctrl);
int_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_INT, scenario_id);
mif_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_MIF, scenario_id);
i2c_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_I2C, scenario_id);
cam_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_CAM, scenario_id);
disp_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_DISP, scenario_id);
hpg_qos = fimc_is_get_qos(core, FIMC_IS_DVFS_HPG, scenario_id);
if ((int_qos < 0) || (mif_qos < 0) || (i2c_qos < 0)
|| (cam_qos < 0) || (disp_qos < 0)) {
err("getting qos value is failed!!\n");
return -EINVAL;
}
/* check current qos */
if (int_qos && dvfs_ctrl->cur_int_qos != int_qos) {
if (i2c_qos && device) {
ret = fimc_is_itf_i2c_lock(device, i2c_qos, true);
if (ret) {
err("fimc_is_itf_i2_clock fail\n");
goto exit;
}
}
pm_qos_update_request(&exynos_isp_qos_int, int_qos);
dvfs_ctrl->cur_int_qos = int_qos;
if (i2c_qos && device) {
/* i2c unlock */
ret = fimc_is_itf_i2c_lock(device, i2c_qos, false);
if (ret) {
err("fimc_is_itf_i2c_unlock fail\n");
goto exit;
}
}
}
if (mif_qos && dvfs_ctrl->cur_mif_qos != mif_qos) {
pm_qos_update_request(&exynos_isp_qos_mem, mif_qos);
dvfs_ctrl->cur_mif_qos = mif_qos;
}
if (cam_qos && dvfs_ctrl->cur_cam_qos != cam_qos) {
pm_qos_update_request(&exynos_isp_qos_cam, cam_qos);
dvfs_ctrl->cur_cam_qos = cam_qos;
}
#if defined(ENABLE_HMP_BOOST)
/* hpg_qos : number of minimum online CPU */
if (hpg_qos && dvfs_ctrl->cur_hpg_qos != hpg_qos) {
pm_qos_update_request(&exynos_isp_qos_hpg, hpg_qos);
dvfs_ctrl->cur_hpg_qos = hpg_qos;
#if defined(CONFIG_HMP_VARIABLE_SCALE)
/* for migration to big core */
if (hpg_qos > 6) {
if (!dvfs_ctrl->cur_hmp_bst) {
set_hmp_boost(1);
dvfs_ctrl->cur_hmp_bst = 1;
}
} else {
if (dvfs_ctrl->cur_hmp_bst) {
set_hmp_boost(0);
dvfs_ctrl->cur_hmp_bst = 0;
}
}
#endif
}
#endif
info("[RSC:%d]: New QoS [INT(%d), MIF(%d), CAM(%d), DISP(%d), I2C(%d), HPG(%d, %d)]\n",
device ? device->instance : 0, int_qos, mif_qos,
cam_qos, disp_qos, i2c_qos, hpg_qos, dvfs_ctrl->cur_hmp_bst);
exit:
return ret;
}
#endif

View file

@ -0,0 +1,85 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DVFS_H
#define FIMC_IS_DVFS_H
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include "fimc-is-time.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
#include "fimc-is-groupmgr.h"
#include "fimc-is-device-ischain.h"
#define DVFS_NOT_MATCHED 0 /* not matched in this scenario */
#define DVFS_MATCHED 1 /* matched in this scenario */
#define DVFS_SKIP 2 /* matched, but do not anything. skip changing dvfs */
#define KEEP_FRAME_TICK_DEFAULT (5)
#define DVFS_SN_STR(__SCENARIO) #__SCENARIO
#define GET_DVFS_CHK_FUNC(__SCENARIO) check_ ## __SCENARIO
#define DECLARE_DVFS_CHK_FUNC(__SCENARIO) \
int check_ ## __SCENARIO \
(struct fimc_is_device_ischain *device, struct fimc_is_group *group, int position, int resol, int fps, int stream_cnt, ...)
#define DECLARE_EXT_DVFS_CHK_FUNC(__SCENARIO) \
int check_ ## __SCENARIO \
(struct fimc_is_device_sensor *device, int position, int resol, int fps, int stream_cnt, ...)
#define GET_KEY_FOR_DVFS_TBL_IDX(__HAL_VER) \
(#__HAL_VER "_TBL_IDX")
#define DECLARE_DVFS_DT(SIZE, ...) \
struct fimc_is_dvfs_dt_t fimc_is_dvfs_dt_arr[SIZE] = {__VA_ARGS__};
#define DECLARE_EXTERN_DVFS_DT(SIZE) \
extern struct fimc_is_dvfs_dt_t fimc_is_dvfs_dt_arr[SIZE];
#define SIZE_FHD (1920 * 1080)
#define SIZE_WHD (2560 * 1440)
#define SIZE_UHD (3840 * 2160)
struct fimc_is_dvfs_dt_t {
const char *parse_scenario_nm; /* string for parsing from DTS */
u32 scenario_id; /* scenario_id */
};
struct fimc_is_dvfs_scenario {
u32 scenario_id; /* scenario_id */
char *scenario_nm; /* string of scenario_id */
int priority; /* priority for dynamic scenario */
int keep_frame_tick; /* keep qos lock during specific frames when dynamic scenario */
/* function pointer to check a scenario */
int (*check_func)(struct fimc_is_device_ischain *device, struct fimc_is_group *group,
int position, int resol, int fps, int stream_cnt, ...);
int (*ext_check_func)(struct fimc_is_device_sensor *device, int position, int resol, int fps, int stream_cnt, ...);
};
struct fimc_is_dvfs_scenario_ctrl {
int cur_scenario_id; /* selected scenario idx */
int cur_frame_tick; /* remained frame tick to keep qos lock in dynamic scenario */
int scenario_cnt; /* total scenario count */
int cur_scenario_idx; /* selected scenario idx for scenarios */
struct fimc_is_dvfs_scenario *scenarios;
};
int fimc_is_dvfs_init(struct fimc_is_resourcemgr *resourcemgr);
int fimc_is_dvfs_sel_table(struct fimc_is_resourcemgr *resourcemgr);
int fimc_is_dvfs_sel_static(struct fimc_is_device_ischain *device);
int fimc_is_dvfs_sel_dynamic(struct fimc_is_device_ischain *device, struct fimc_is_group *group);
int fimc_is_dvfs_sel_external(struct fimc_is_device_sensor *device);
int fimc_is_get_qos(struct fimc_is_core *core, u32 type, u32 scenario_id);
int fimc_is_set_dvfs(struct fimc_is_core *core, struct fimc_is_device_ischain *device, u32 scenario_id);
#endif

View file

@ -0,0 +1,305 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_ERR_H
#define FIMC_IS_ERR_H
#define IS_ERROR_VER 014 /* IS ERROR VERSION 0.14 */
#define IS_ERROR_SUCCESS 0
/* General 1 ~ 100 */
#define IS_ERROR_INVALID_COMMAND (IS_ERROR_SUCCESS+1)
#define IS_ERROR_REQUEST_FAIL (IS_ERROR_INVALID_COMMAND+1)
#define IS_ERROR_INVALID_SCENARIO (IS_ERROR_REQUEST_FAIL+1)
#define IS_ERROR_INVALID_SENSORID (IS_ERROR_INVALID_SCENARIO+1)
#define IS_ERROR_INVALID_MODE_CHANGE (IS_ERROR_INVALID_SENSORID+1)
#define IS_ERROR_INVALID_MAGIC_NUMBER (IS_ERROR_INVALID_MODE_CHANGE+1)
#define IS_ERROR_INVALID_SETFILE_HDR (IS_ERROR_INVALID_MAGIC_NUMBER+1)
#define IS_ERROR_ISP_SETFILE_VERSION_MISMATCH (IS_ERROR_INVALID_SETFILE_HDR+1)
#define IS_ERROR_ISP_SETFILE_REVISION_MISMATCH\
(IS_ERROR_ISP_SETFILE_VERSION_MISMATCH+1)
#define IS_ERROR_BUSY (IS_ERROR_ISP_SETFILE_REVISION_MISMATCH+1)
#define IS_ERROR_SET_PARAMETER (IS_ERROR_BUSY+1)
#define IS_ERROR_INVALID_PATH (IS_ERROR_SET_PARAMETER+1)
#define IS_ERROR_OPEN_SENSOR_FAIL (IS_ERROR_INVALID_PATH+1)
#define IS_ERROR_ENTRY_MSG_THREAD_DOWN (IS_ERROR_OPEN_SENSOR_FAIL+1)
#define IS_ERROR_ISP_FRAME_END_NOT_DONE (IS_ERROR_ENTRY_MSG_THREAD_DOWN+1)
#define IS_ERROR_DRC_FRAME_END_NOT_DONE (IS_ERROR_ISP_FRAME_END_NOT_DONE+1)
#define IS_ERROR_SCALERC_FRAME_END_NOT_DONE (IS_ERROR_DRC_FRAME_END_NOT_DONE+1)
#define IS_ERROR_DIS_FRAME_END_NOT_DONE (IS_ERROR_SCALERC_FRAME_END_NOT_DONE+1)
#define IS_ERROR_TDNR_FRAME_END_NOT_DONE (IS_ERROR_DIS_FRAME_END_NOT_DONE+1)
#define IS_ERROR_SCALERP_FRAME_END_NOT_DONE (IS_ERROR_TDNR_FRAME_END_NOT_DONE+1)
#define IS_ERROR_WAIT_STREAM_OFF_NOT_DONE\
(IS_ERROR_SCALERP_FRAME_END_NOT_DONE+1)
#define IS_ERROR_NO_MSG_IS_RECEIVED (IS_ERROR_WAIT_STREAM_OFF_NOT_DONE+1)
#define IS_ERROR_SENSOR_MSG_FAIL (IS_ERROR_NO_MSG_IS_RECEIVED+1)
#define IS_ERROR_ISP_MSG_FAIL (IS_ERROR_SENSOR_MSG_FAIL+1)
#define IS_ERROR_DRC_MSG_FAIL (IS_ERROR_ISP_MSG_FAIL+1)
#define IS_ERROR_SCALERC_MSG_FAIL (IS_ERROR_DRC_MSG_FAIL+1)
#define IS_ERROR_DIS_MSG_FAIL (IS_ERROR_ODC_MSG_FAIL+1)
#define IS_ERROR_TDNR_MSG_FAIL (IS_ERROR_DIS_MSG_FAIL+1)
#define IS_ERROR_SCALERP_MSG_FAIL (IS_ERROR_TDNR_MSG_FAIL+1)
#define IS_ERROR_LHFD_MSG_FAIL (IS_ERROR_SCALERP_MSG_FAIL+1)
#define IS_ERROR_INTERNAL_STOP (IS_ERROR_LHFD_MSG_FAIL+1)
#define IS_ERROR_UNKNOWN 1000
#define IS_ERROR_TIME_OUT_FLAG 0x80000000
/* Sensor 100 ~ 200 */
#define IS_ERROR_SENSOR_PWRDN_FAIL 100
#define IS_ERROR_SENSOR_STREAM_ON_FAIL (IS_ERROR_SENSOR_PWRDN_FAIL+1)
#define IS_ERROR_SENSOR_STREAM_OFF_FAIL (IS_ERROR_SENSOR_STREAM_ON_FAIL+1)
/* ISP 200 ~ 300 */
#define IS_ERROR_ISP_PWRDN_FAIL 200
#define IS_ERROR_ISP_MULTIPLE_INPUT (IS_ERROR_ISP_PWRDN_FAIL+1)
#define IS_ERROR_ISP_ABSENT_INPUT (IS_ERROR_ISP_MULTIPLE_INPUT+1)
#define IS_ERROR_ISP_ABSENT_OUTPUT (IS_ERROR_ISP_ABSENT_INPUT+1)
#define IS_ERROR_ISP_NONADJACENT_OUTPUT (IS_ERROR_ISP_ABSENT_OUTPUT+1)
#define IS_ERROR_ISP_FORMAT_MISMATCH (IS_ERROR_ISP_NONADJACENT_OUTPUT+1)
#define IS_ERROR_ISP_WIDTH_MISMATCH (IS_ERROR_ISP_FORMAT_MISMATCH+1)
#define IS_ERROR_ISP_HEIGHT_MISMATCH (IS_ERROR_ISP_WIDTH_MISMATCH+1)
#define IS_ERROR_ISP_BITWIDTH_MISMATCH (IS_ERROR_ISP_HEIGHT_MISMATCH+1)
#define IS_ERROR_ISP_FRAME_END_TIME_OUT (IS_ERROR_ISP_BITWIDTH_MISMATCH+1)
/* DRC 300 ~ 400 */
#define IS_ERROR_DRC_PWRDN_FAIL 300
#define IS_ERROR_DRC_MULTIPLE_INPUT (IS_ERROR_DRC_PWRDN_FAIL+1)
#define IS_ERROR_DRC_ABSENT_INPUT (IS_ERROR_DRC_MULTIPLE_INPUT+1)
#define IS_ERROR_DRC_NONADJACENT_INTPUT (IS_ERROR_DRC_ABSENT_INPUT+1)
#define IS_ERROR_DRC_ABSENT_OUTPUT (IS_ERROR_DRC_NONADJACENT_INTPUT+1)
#define IS_ERROR_DRC_NONADJACENT_OUTPUT (IS_ERROR_DRC_ABSENT_OUTPUT+1)
#define IS_ERROR_DRC_FORMAT_MISMATCH (IS_ERROR_DRC_NONADJACENT_OUTPUT+1)
#define IS_ERROR_DRC_WIDTH_MISMATCH (IS_ERROR_DRC_FORMAT_MISMATCH+1)
#define IS_ERROR_DRC_HEIGHT_MISMATCH (IS_ERROR_DRC_WIDTH_MISMATCH+1)
#define IS_ERROR_DRC_BITWIDTH_MISMATCH (IS_ERROR_DRC_HEIGHT_MISMATCH+1)
#define IS_ERROR_DRC_FRAME_END_TIME_OUT (IS_ERROR_DRC_BITWIDTH_MISMATCH+1)
/*SCALERC(400~500)*/
#define IS_ERROR_SCALERC_PWRDN_FAIL 400
/*DIS(600~700)*/
#define IS_ERROR_DIS_PWRDN_FAIL 600
/*TDNR(700~800) */
#define IS_ERROR_TDNR_PWRDN_FAIL 700
/*SCALERP(800~900)*/
#define IS_ERROR_SCALERP_PWRDN_FAIL 800
/*FD(900~1000)*/
#define IS_ERROR_FD_PWRDN_FAIL 900
#define IS_ERROR_FD_MULTIPLE_INPUT (IS_ERROR_FD_PWRDN_FAIL+1)
#define IS_ERROR_FD_ABSENT_INPUT (IS_ERROR_FD_MULTIPLE_INPUT+1)
#define IS_ERROR_FD_NONADJACENT_INPUT (IS_ERROR_FD_ABSENT_INPUT+1)
#define IS_ERROR_LHFD_FRAME_END_TIME_OUT \
(IS_ERROR_FD_NONADJACENT_INPUT+1)
/* Set parameter error enum */
enum error {
/* Common error (0~99) */
ERROR_COMMON_NO = 0,
ERROR_COMMON_CMD = 1, /* Invalid command*/
ERROR_COMMON_PARAMETER = 2, /* Invalid parameter*/
/* setfile is not loaded before adjusting */
ERROR_COMMON_SETFILE_LOAD = 3,
/* setfile is not Adjusted before runnng. */
ERROR_COMMON_SETFILE_ADJUST = 4,
/* index of setfile is not valid. */
ERROR_COMMON_SETFILE_INDEX = 5,
/* Input path can be changed in ready state(stop) */
ERROR_COMMON_INPUT_PATH = 6,
/* IP can not start if input path is not set */
ERROR_COMMON_INPUT_INIT = 7,
/* Output path can be changed in ready state(stop) */
ERROR_COMMON_OUTPUT_PATH = 8,
/* IP can not start if output path is not set */
ERROR_COMMON_OUTPUT_INIT = 9,
ERROR_CONTROL_NO = ERROR_COMMON_NO,
ERROR_CONTROL_BYPASS = 11, /* Enable or Disable */
ERROR_CONTROL_BUF = 12, /* invalid buffer info */
ERROR_OTF_INPUT_NO = ERROR_COMMON_NO,
/* invalid command */
ERROR_OTF_INPUT_CMD = 21,
/* invalid format (DRC: YUV444, FD: YUV444, 422, 420) */
ERROR_OTF_INPUT_FORMAT = 22,
/* invalid width (DRC: 128~8192, FD: 32~8190) */
ERROR_OTF_INPUT_WIDTH = 23,
/* invalid height (DRC: 64~8192, FD: 16~8190) */
ERROR_OTF_INPUT_HEIGHT = 24,
/* invalid bit-width (DRC: 8~12bits, FD: 8bit) */
ERROR_OTF_INPUT_BIT_WIDTH = 25,
/* invalid frame time for ISP */
ERROR_OTF_INPUT_USER_FRAMETILE = 26,
ERROR_DMA_INPUT_NO = ERROR_COMMON_NO,
/* invalid width (DRC: 128~8192, FD: 32~8190) */
ERROR_DMA_INPUT_WIDTH = 31,
/* invalid height (DRC: 64~8192, FD: 16~8190) */
ERROR_DMA_INPUT_HEIGHT = 32,
/* invalid format (DRC: YUV444 or YUV422, FD: YUV444, 422, 420) */
ERROR_DMA_INPUT_FORMAT = 33,
/* invalid bit-width (DRC: 8~12bit, FD: 8bit) */
ERROR_DMA_INPUT_BIT_WIDTH = 34,
/* invalid order(DRC: YYCbCrorYCbYCr, FD:NO,YYCbCr,YCbYCr,CbCr,CrCb) */
ERROR_DMA_INPUT_ORDER = 35,
/* invalid palne (DRC: 3, FD: 1, 2, 3) */
ERROR_DMA_INPUT_PLANE = 36,
ERROR_OTF_OUTPUT_NO = ERROR_COMMON_NO,
/* invalid width (DRC: 128~8192) */
ERROR_OTF_OUTPUT_WIDTH = 41,
/* invalid height (DRC: 64~8192) */
ERROR_OTF_OUTPUT_HEIGHT = 42,
/* invalid format (DRC: YUV444) */
ERROR_OTF_OUTPUT_FORMAT = 43,
/* invalid bit-width (DRC: 8~12bits) */
ERROR_OTF_OUTPUT_BIT_WIDTH = 44,
/* invalid crop size (ODC: left>2, right>10) */
ERROR_OTF_OUTPUT_CROP = 45,
ERROR_DMA_OUTPUT_NO = ERROR_COMMON_NO,
ERROR_DMA_OUTPUT_WIDTH = 51, /* invalid width */
ERROR_DMA_OUTPUT_HEIGHT = 52, /* invalid height */
ERROR_DMA_OUTPUT_FORMAT = 53, /* invalid format */
ERROR_DMA_OUTPUT_BIT_WIDTH = 54, /* invalid bit-width */
ERROR_DMA_OUTPUT_PLANE = 55, /* invalid plane */
ERROR_DMA_OUTPUT_ORDER = 56, /* invalid order */
ERROR_DMA_OUTPUT_BUF = 57, /* invalid buffer info */
ERROR_GLOBAL_SHOTMODE_NO = ERROR_COMMON_NO,
/* SENSOR Error(100~199) */
ERROR_SENSOR_NO = ERROR_COMMON_NO,
ERROR_SENSOR_I2C_FAIL = 101,
ERROR_SENSOR_INVALID_FRAMERATE,
ERROR_SENSOR_INVALID_EXPOSURETIME,
ERROR_SENSOR_INVALID_SIZE,
ERROR_SENSOR_ACTURATOR_INIT_FAIL,
ERROR_SENSOR_INVALID_AF_POS,
ERROR_SENSOR_UNSUPPORT_FUNC,
ERROR_SENSOR_UNSUPPORT_PERI,
ERROR_SENSOR_UNSUPPORT_AF,
ERROR_SENSOR_FLASH_FAIL,
ERROR_SENSOR_START_FAIL,
ERROR_SENSOR_STOP_FAIL,
/* ISP Error (200~299) */
ERROR_ISP_AF_NO = ERROR_COMMON_NO,
ERROR_ISP_AF_BUSY = 201,
ERROR_ISP_AF_INVALID_COMMAND = 202,
ERROR_ISP_AF_INVALID_MODE = 203,
ERROR_ISP_FLASH_NO = ERROR_COMMON_NO,
ERROR_ISP_AWB_NO = ERROR_COMMON_NO,
ERROR_ISP_IMAGE_EFFECT_NO = ERROR_COMMON_NO,
ERROR_ISP_IMAGE_EFFECT_INVALID = 231,
ERROR_ISP_ISO_NO = ERROR_COMMON_NO,
ERROR_ISP_ADJUST_NO = ERROR_COMMON_NO,
ERROR_ISP_METERING_NO = ERROR_COMMON_NO,
ERROR_ISP_AFC_NO = ERROR_COMMON_NO,
/* DRC Error (300~399) */
/* FD Error (400~499) */
ERROR_FD_NO = ERROR_COMMON_NO,
/* Invalid max number (1~16) */
ERROR_FD_CONFIG_MAX_NUMBER_STATE = 401,
ERROR_FD_CONFIG_MAX_NUMBER_INVALID = 402,
ERROR_FD_CONFIG_YAW_ANGLE_STATE = 403,
ERROR_FD_CONFIG_YAW_ANGLE_INVALID = 404,
ERROR_FD_CONFIG_ROLL_ANGLE_STATE = 405,
ERROR_FD_CONFIG_ROLL_ANGLE_INVALID = 406,
ERROR_FD_CONFIG_SMILE_MODE_INVALID = 407,
ERROR_FD_CONFIG_BLINK_MODE_INVALID = 408,
ERROR_FD_CONFIG_EYES_DETECT_INVALID = 409,
ERROR_FD_CONFIG_MOUTH_DETECT_INVALID = 410,
ERROR_FD_CONFIG_ORIENTATION_STATE = 411,
ERROR_FD_CONFIG_ORIENTATION_INVALID = 412,
ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID = 413,
/* PARAM_FdResultStr can be only applied
* in ready-state or stream off */
ERROR_FD_RESULT = 414,
/* PARAM_FdModeStr can be only applied
* in ready-state or stream off */
ERROR_FD_MODE = 415,
/*SCALER ERR(500~599)*/
ERROR_SCALER_NO = ERROR_COMMON_NO,
ERROR_SCALER_DMA_OUTSEL = 501,
ERROR_SCALER_H_RATIO = 502,
ERROR_SCALER_V_RATIO = 503,
ERROR_SCALER_FRAME_BUFFER_SEQ = 504,
ERROR_SCALER_IMAGE_EFFECT = 510,
ERROR_SCALER_ROTATE = 520,
ERROR_SCALER_FLIP = 521,
};
enum ShotErrorType {
IS_SHOT_SUCCESS = 0,
/* Un-known state.(Normally under processing.) */
IS_SHOT_UNKNOWN,
/* Bad frame. Ndone is occured at provious group. */
IS_SHOT_BAD_FRAME,
/* Metadata is not valid. For example, sirc sdk's fd is not valid. */
IS_SHOT_CORRUPTED_FRAME,
/* Processing of previous group is not complete. */
IS_SHOT_EARLY_FRAME,
/* Shot is too late at OTF mode. */
IS_SHOT_LATE_FRAME,
/* Shot is coming when group is process-stop. */
IS_SHOT_GROUP_PROCESSSTOP,
/* Frame number is not allocated. */
IS_SHOT_INVALID_FRAMENUMBER,
/* Overflow is occred during processing. */
IS_SHOT_OVERFLOW,
/* Shot is time-out during processing.(Unknown reason.) */
IS_SHOT_SAME_FRAME_COUNT,
/* Shot is time-out during processing.(Unknown reason.) */
IS_SHOT_TIMEOUT,
/* Shot is droped at BufferEntry. */
IS_SHOT_DROP,
IS_SHOT_3AA_FRAME_END,
IS_SHOT_IP_CLOCK_OFF,
IS_SHOT_ENDPROC_DELAY,
IS_SHOT_UNPROCESSED, /* Shot is coming in advance at PROCESS_STOP command */
IS_SHOT_INSUFFICIENT_RESOURCES, /* FrameDescriptor is overflow */
IS_SHOT_3AA_ERROR, /* 3AA error except for overflow */
SHOT_ERR_MISMATCH, /* host error code */
SHOT_ERR_PERFRAME
};
enum REPORT_ERR_TYPE {
REPORT_ERR_CIS_ID = 0,
REPORT_ERR_CIS_CRC = 1,
REPORT_ERR_CIS_ECC = 2,
REPORT_ERR_CIS_OVERFLOW_VC0 = 3,
REPORT_ERR_CIS_LOST_FE_VC0 = 4,
REPORT_ERR_CIS_LOST_FS_VC0 = 5,
REPORT_ERR_CIS_SOT_VC0 = 6,
REPORT_ERR_CIS_SOT_VC1 = 7,
REPORT_ERR_CIS_SOT_VC2 = 8,
REPORT_ERR_CIS_SOT_VC3 = 9,
REPORT_ERR_3AA_OVERFLOW = 10,
REPORT_ERR_FLITE_D_OVERFLOW = 11,
REPORT_ERR_PREPROCESSOR_LOST_FRAME = 12,
REPORT_ERR_3A_ALGORITHM_DELAYED = 13,
REPORT_ERR_END
};
#define ENOBASE_IS 0x10000
#define ENOSHOT (ENOBASE_IS + 1) /* shot error */
#define ENOMDONE (ENOBASE_IS + 2) /* meta done error */
#endif

View file

@ -0,0 +1,339 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
#include "fimc-is-device-sensor.h"
static const char * const frame_state_name[NR_FRAME_STATE] = {
"Free",
"Request",
"Process",
"Complete"
};
int frame_fcount(struct fimc_is_frame *frame, void *data)
{
return frame->fcount - (u32)(ulong)data;
}
int put_frame(struct fimc_is_framemgr *this, struct fimc_is_frame *frame,
enum fimc_is_frame_state state)
{
if (state == FS_INVALID)
return -EINVAL;
if (!frame) {
err("invalid frame");
return -EFAULT;
}
frame->state = state;
list_add_tail(&frame->list, &this->queued_list[state]);
this->queued_count[state]++;
#ifdef TRACE_FRAME
print_frame_queue(this, state);
#endif
return 0;
}
struct fimc_is_frame *get_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
struct fimc_is_frame *frame;
if (state == FS_INVALID)
return NULL;
if (!this->queued_count[state]) {
err("%s frame queue is empty", frame_state_name[state]);
return NULL;
}
frame = list_first_entry(&this->queued_list[state],
struct fimc_is_frame, list);
list_del(&frame->list);
this->queued_count[state]--;
frame->state = FS_INVALID;
return frame;
}
int trans_frame(struct fimc_is_framemgr *this, struct fimc_is_frame *frame,
enum fimc_is_frame_state state)
{
if (!frame) {
err("invalid frame");
return -EFAULT;
}
if ((frame->state == FS_INVALID) || (state == FS_INVALID))
return -EINVAL;
if (!this->queued_count[frame->state]) {
err("%s frame queue is empty", frame_state_name[frame->state]);
return -EINVAL;
}
list_del(&frame->list);
this->queued_count[frame->state]--;
if (state == FS_PROCESS && (!(this->id & FRAMEMGR_ID_HW)))
frame->bak_flag = frame->out_flag;
return put_frame(this, frame, state);
}
struct fimc_is_frame *peek_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
if (state == FS_INVALID)
return NULL;
if (!this->queued_count[state])
return NULL;
return list_first_entry(&this->queued_list[state],
struct fimc_is_frame, list);
}
struct fimc_is_frame *peek_frame_tail(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
if (state == FS_INVALID)
return NULL;
if (!this->queued_count[state])
return NULL;
return list_last_entry(&this->queued_list[state],
struct fimc_is_frame, list);
}
struct fimc_is_frame *find_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state,
int (*fn)(struct fimc_is_frame *, void *), void *data)
{
struct fimc_is_frame *frame;
if (state == FS_INVALID)
return NULL;
if (!this->queued_count[state]) {
err("%s frame queue is empty", frame_state_name[state]);
return NULL;
}
list_for_each_entry(frame, &this->queued_list[state], list) {
if (!fn(frame, data))
return frame;
}
return NULL;
}
void print_frame_queue(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
struct fimc_is_frame *frame, *temp;
if (!(TRACE_ID & this->id))
return;
pr_info("[FRM] %s(0x%08x, %d) :", frame_state_name[state],
this->id, this->queued_count[state]);
list_for_each_entry_safe(frame, temp, &this->queued_list[state], list)
pr_cont("%d[%d]->", frame->index, frame->fcount);
pr_cont("X\n");
}
#ifndef ENABLE_IS_CORE
void print_frame_info_queue(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
unsigned long long when[MAX_FRAME_INFO];
unsigned long usec[MAX_FRAME_INFO];
struct fimc_is_frame *frame, *temp;
if (!(TRACE_ID & this->id))
return;
pr_info("[FRM_INFO] %s(0x%08x, %d) :", frame_state_name[state],
this->id, this->queued_count[state]);
list_for_each_entry_safe(frame, temp, &this->queued_list[state], list) {
when[INFO_FRAME_START] = frame->frame_info[INFO_FRAME_START].when;
when[INFO_CONFIG_LOCK] = frame->frame_info[INFO_CONFIG_LOCK].when;
when[INFO_FRAME_END_PROC] = frame->frame_info[INFO_FRAME_END_PROC].when;
usec[INFO_FRAME_START] = do_div(when[INFO_FRAME_START], NSEC_PER_SEC);
usec[INFO_CONFIG_LOCK] = do_div(when[INFO_CONFIG_LOCK], NSEC_PER_SEC);
usec[INFO_FRAME_END_PROC] = do_div(when[INFO_FRAME_END_PROC], NSEC_PER_SEC);
pr_cont("%d[%d][%d]([%5lu.%06lu],[%5lu.%06lu],[%5lu.%06lu][C:0x%lx])->",
frame->index, frame->fcount, frame->type,
(unsigned long)when[INFO_FRAME_START], usec[INFO_FRAME_START] / NSEC_PER_USEC,
(unsigned long)when[INFO_CONFIG_LOCK], usec[INFO_CONFIG_LOCK] / NSEC_PER_USEC,
(unsigned long)when[INFO_FRAME_END_PROC], usec[INFO_FRAME_END_PROC] / NSEC_PER_USEC,
frame->core_flag);
}
pr_cont("X\n");
}
#endif
int frame_manager_probe(struct fimc_is_framemgr *this, u32 id)
{
this->id = id;
spin_lock_init(&this->slock);
this->frames = NULL;
return 0;
}
static void default_frame_work_fn(struct kthread_work *work)
{
struct fimc_is_groupmgr *groupmgr;
struct fimc_is_group *group;
struct fimc_is_frame *frame;
frame = container_of(work, struct fimc_is_frame, work);
groupmgr = frame->groupmgr;
group = frame->group;
fimc_is_group_shot(groupmgr, group, frame);
}
int frame_manager_open(struct fimc_is_framemgr *this, u32 buffers)
{
u32 i;
unsigned long flag;
/*
* We already have frames allocated, so we should free them first.
* reqbufs(n) could be called multiple times from userspace after
* each video node was opened.
*/
if (this->frames)
kfree(this->frames);
this->frames = kzalloc(sizeof(struct fimc_is_frame) * buffers,
GFP_KERNEL);
if (!this->frames) {
err("failed to allocate frames");
return -ENOMEM;
}
spin_lock_irqsave(&this->slock, flag);
this->num_frames = buffers;
for (i = 0; i < NR_FRAME_STATE; i++) {
this->queued_count[i] = 0;
INIT_LIST_HEAD(&this->queued_list[i]);
}
for (i = 0; i < buffers; ++i) {
this->frames[i].index = i;
put_frame(this, &this->frames[i], FS_FREE);
init_kthread_work(&this->frames[i].work, default_frame_work_fn);
}
spin_unlock_irqrestore(&this->slock, flag);
return 0;
}
int frame_manager_close(struct fimc_is_framemgr *this)
{
unsigned long flag;
spin_lock_irqsave(&this->slock, flag);
if (this->frames) {
kfree(this->frames);
this->frames = NULL;
}
spin_unlock_irqrestore(&this->slock, flag);
return 0;
}
int frame_manager_flush(struct fimc_is_framemgr *this)
{
unsigned long flag;
struct fimc_is_frame *frame, *temp;
enum fimc_is_frame_state i;
spin_lock_irqsave(&this->slock, flag);
for (i = FS_REQUEST; i < FS_INVALID; i++) {
list_for_each_entry_safe(frame, temp, &this->queued_list[i], list)
trans_frame(this, frame, FS_FREE);
}
spin_unlock_irqrestore(&this->slock, flag);
BUG_ON(this->queued_count[FS_FREE] != this->num_frames);
return 0;
}
void frame_manager_print_queues(struct fimc_is_framemgr *this)
{
int i;
for (i = 0; i < NR_FRAME_STATE; i++)
print_frame_queue(this, (enum fimc_is_frame_state)i);
}
void frame_manager_print_info_queues(struct fimc_is_framemgr *this)
{
#ifndef ENABLE_IS_CORE
int i;
for (i = 0; i < NR_FRAME_STATE; i++)
print_frame_info_queue(this, (enum fimc_is_frame_state)i);
#endif
}

View file

@ -0,0 +1,238 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_FRAME_MGR_H
#define FIMC_IS_FRAME_MGR_H
#include <linux/kthread.h>
#include "fimc-is-time.h"
#define FIMC_IS_MAX_BUFS VIDEO_MAX_FRAME
#define FIMC_IS_MAX_PLANES VIDEO_MAX_PLANES
/*matching for xxxTargetAddress in struct camera2_scaler_uctl */
#define FIMC_IS_MAX_TARGET_ADDR 4
#define FRAMEMGR_ID_INVALID 0x0000000
#define FRAMEMGR_ID_SSX 0x0000100
#define FRAMEMGR_ID_SSXVC0 0x0000011
#define FRAMEMGR_ID_SSXVC1 0x0000012
#define FRAMEMGR_ID_SSXVC2 0x0000013
#define FRAMEMGR_ID_SSXVC3 0x0000014
#define FRAMEMGR_ID_3XS 0x0000200
#define FRAMEMGR_ID_3XC 0x0000400
#define FRAMEMGR_ID_3XP 0x0000800
#define FRAMEMGR_ID_IXS 0x0001000
#define FRAMEMGR_ID_IXC 0x0002000
#define FRAMEMGR_ID_IXP 0x0004000
#define FRAMEMGR_ID_DIS 0x0008000
#define FRAMEMGR_ID_SCC 0x0010000
#define FRAMEMGR_ID_SCP 0x0020000
#define FRAMEMGR_ID_MXS 0x0040000
#define FRAMEMGR_ID_M0P 0x0080000
#define FRAMEMGR_ID_M1P 0x0100000
#define FRAMEMGR_ID_M2P 0x0200000
#define FRAMEMGR_ID_M3P 0x0400000
#define FRAMEMGR_ID_M4P 0x0800000
#define FRAMEMGR_ID_VRA 0x1000000
#define FRAMEMGR_ID_HW 0x2000000
#define FRAMEMGR_ID_SHOT (FRAMEMGR_ID_SSX | FRAMEMGR_ID_3XS | \
FRAMEMGR_ID_IXS | FRAMEMGR_ID_DIS | \
FRAMEMGR_ID_MXS | FRAMEMGR_ID_VRA)
#define FRAMEMGR_ID_STREAM (FRAMEMGR_ID_3XC | FRAMEMGR_ID_3XP | \
FRAMEMGR_ID_SCC | FRAMEMGR_ID_DIS | \
FRAMEMGR_ID_SCP | FRAMEMGR_ID_M0P | \
FRAMEMGR_ID_M1P | FRAMEMGR_ID_M2P | \
FRAMEMGR_ID_M3P | FRAMEMGR_ID_M4P | \
FRAMEMGR_ID_SSXVC0 | FRAMEMGR_ID_SSXVC1 | \
FRAMEMGR_ID_SSXVC2 | FRAMEMGR_ID_SSXVC3)
/* #define TRACE_FRAME */
#define TRACE_ID (FRAMEMGR_ID_SHOT | FRAMEMGR_ID_STREAM | FRAMEMGR_ID_HW)
#define FMGR_IDX_0 (1 << 0 )
#define FMGR_IDX_1 (1 << 1 )
#define FMGR_IDX_2 (1 << 2 )
#define FMGR_IDX_3 (1 << 3 )
#define FMGR_IDX_4 (1 << 4 )
#define FMGR_IDX_5 (1 << 5 )
#define FMGR_IDX_6 (1 << 6 )
#define FMGR_IDX_7 (1 << 7 )
#define FMGR_IDX_8 (1 << 8 )
#define FMGR_IDX_9 (1 << 9 )
#define FMGR_IDX_10 (1 << 10)
#define FMGR_IDX_11 (1 << 11)
#define FMGR_IDX_12 (1 << 12)
#define FMGR_IDX_13 (1 << 13)
#define FMGR_IDX_14 (1 << 14)
#define FMGR_IDX_15 (1 << 15)
#define FMGR_IDX_16 (1 << 16)
#define FMGR_IDX_17 (1 << 17)
#define FMGR_IDX_18 (1 << 18)
#define FMGR_IDX_19 (1 << 19)
#define FMGR_IDX_20 (1 << 20)
#define FMGR_IDX_21 (1 << 21)
#define FMGR_IDX_22 (1 << 22)
#define FMGR_IDX_23 (1 << 23)
#define FMGR_IDX_24 (1 << 24)
#define FMGR_IDX_25 (1 << 25)
#define FMGR_IDX_26 (1 << 26)
#define FMGR_IDX_27 (1 << 27)
#define FMGR_IDX_28 (1 << 28)
#define FMGR_IDX_29 (1 << 29)
#define FMGR_IDX_30 (1 << 30)
#define FMGR_IDX_31 (1 << 31)
#define framemgr_e_barrier_irqs(this, index, flag) \
this->sindex |= index; spin_lock_irqsave(&this->slock, flag)
#define framemgr_x_barrier_irqr(this, index, flag) \
spin_unlock_irqrestore(&this->slock, flag); this->sindex &= ~index
#define framemgr_e_barrier_irq(this, index) \
this->sindex |= index; spin_lock_irq(&this->slock)
#define framemgr_x_barrier_irq(this, index) \
spin_unlock_irq(&this->slock); this->sindex &= ~index
#define framemgr_e_barrier(this, index) \
this->sindex |= index; spin_lock(&this->slock)
#define framemgr_x_barrier(this, index) \
spin_unlock(&this->slock); this->sindex &= ~index
enum fimc_is_frame_state {
FS_FREE,
FS_REQUEST,
FS_PROCESS,
FS_COMPLETE,
FS_INVALID
};
enum fimc_is_hw_frame_state {
FS_HW_FREE,
FS_HW_REQUEST,
FS_HW_CONFIGURE,
FS_HW_WAIT_DONE,
FS_HW_INVALID
};
#define NR_FRAME_STATE FS_INVALID
enum fimc_is_frame_mem_state {
/* initialized memory */
FRAME_MEM_INIT,
/* mapped memory */
FRAME_MEM_MAPPED
};
#ifndef ENABLE_IS_CORE
#define MAX_FRAME_INFO (4)
enum fimc_is_frame_info_index {
INFO_FRAME_START,
INFO_CONFIG_LOCK,
INFO_FRAME_END_PROC
};
struct fimc_is_frame_info {
int cpu;
int pid;
unsigned long long when;
};
#endif
struct fimc_is_frame {
struct list_head list;
struct kthread_work work;
void *groupmgr;
void *group;
void *subdev; /* fimc_is_subdev */
/* group leader use */
struct camera2_shot *shot;
struct camera2_shot_ext *shot_ext;
ulong kvaddr_shot;
u32 dvaddr_shot;
ulong cookie_shot;
size_t shot_size;
/* stream use */
struct camera2_stream *stream;
/* common use */
u32 planes;
u32 dvaddr_buffer[FIMC_IS_MAX_PLANES];
ulong kvaddr_buffer[FIMC_IS_MAX_PLANES];
/* internal use */
unsigned long mem_state;
u32 state;
u32 fcount;
u32 rcount;
u32 index;
u32 lindex;
u32 hindex;
u32 result;
unsigned long out_flag;
unsigned long bak_flag;
#ifndef ENABLE_IS_CORE
struct fimc_is_frame_info frame_info[MAX_FRAME_INFO];
u32 instance; /* device instance */
u32 type;
unsigned long core_flag;
atomic_t shot_done_flag;
#endif
#ifdef MEASURE_TIME
/* time measure externally */
struct timeval *tzone;
/* time measure internally */
struct fimc_is_monitor mpoint[TMM_END];
#endif
#ifdef DBG_DRAW_DIGIT
u32 width;
u32 height;
#endif
};
struct fimc_is_framemgr {
u32 id;
spinlock_t slock;
ulong sindex;
u32 num_frames;
struct fimc_is_frame *frames;
u32 queued_count[NR_FRAME_STATE];
struct list_head queued_list[NR_FRAME_STATE];
};
int frame_fcount(struct fimc_is_frame *frame, void *data);
int put_frame(struct fimc_is_framemgr *this, struct fimc_is_frame *frame,
enum fimc_is_frame_state state);
struct fimc_is_frame *get_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state);
int trans_frame(struct fimc_is_framemgr *this, struct fimc_is_frame *frame,
enum fimc_is_frame_state state);
struct fimc_is_frame *peek_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state);
struct fimc_is_frame *peek_frame_tail(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state);
struct fimc_is_frame *find_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state,
int (*fn)(struct fimc_is_frame *, void *), void *data);
void print_frame_queue(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state);
int frame_manager_probe(struct fimc_is_framemgr *this, u32 id);
int frame_manager_open(struct fimc_is_framemgr *this, u32 buffers);
int frame_manager_close(struct fimc_is_framemgr *this);
int frame_manager_flush(struct fimc_is_framemgr *this);
void frame_manager_print_queues(struct fimc_is_framemgr *this);
void frame_manager_print_info_queues(struct fimc_is_framemgr *this);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,291 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_GROUP_MGR_H
#define FIMC_IS_GROUP_MGR_H
#include "fimc-is-config.h"
#include "fimc-is-time.h"
#include "fimc-is-subdev-ctrl.h"
#include "fimc-is-video.h"
#include "fimc-is-cmd.h"
/* #define DEBUG_AA */
/* #define DEBUG_FLASH */
#define GROUP_STREAM_INVALID 0xFFFFFFFF
#ifdef CONFIG_USE_SENSOR_GROUP
#define TRACE_GROUP
#define GROUP_ID_3AA0 0
#define GROUP_ID_3AA1 1
#define GROUP_ID_ISP0 2
#define GROUP_ID_ISP1 3
#define GROUP_ID_DIS0 4
#define GROUP_ID_MCS0 5
#define GROUP_ID_MCS1 6
#define GROUP_ID_VRA0 7
#define GROUP_ID_SS0 8
#define GROUP_ID_SS1 9
#define GROUP_ID_SS2 10
#define GROUP_ID_SS3 11
#define GROUP_ID_SS4 12
#define GROUP_ID_SS5 13
#define GROUP_ID_MAX 14
#define GROUP_ID_INVALID 0xFFFFFFFF
#define GROUP_ID_PARM_MASK (0xFF)
#define GROUP_ID_SHIFT (16)
#define GROUP_ID_MASK (0xFFFF)
#define GROUP_ID(id) (1 << (id))
#define GROUP_SLOT_SENSOR 0
#define GROUP_SLOT_3AA 1
#define GROUP_SLOT_ISP 2
#define GROUP_SLOT_DIS 3
#define GROUP_SLOT_MCS 4
#define GROUP_SLOT_VRA 5
#define GROUP_SLOT_MAX 6
#else
#define TRACE_GROUP
#define GROUP_ID_3AA0 0
#define GROUP_ID_3AA1 1
#define GROUP_ID_ISP0 2
#define GROUP_ID_ISP1 3
#define GROUP_ID_DIS0 4
#define GROUP_ID_MCS0 5
#define GROUP_ID_MCS1 6
#define GROUP_ID_VRA0 7
#define GROUP_ID_MAX 8
#define GROUP_ID_INVALID 0xFFFFFFFF
#define GROUP_ID_PARM_MASK (0xFF)
#define GROUP_ID_SHIFT (16)
#define GROUP_ID_MASK (0xFFFF)
#define GROUP_ID(id) (1 << (id))
#define GROUP_SLOT_3AA 0
#define GROUP_SLOT_ISP 1
#define GROUP_SLOT_DIS 2
#define GROUP_SLOT_MCS 3
#define GROUP_SLOT_VRA 4
#define GROUP_SLOT_MAX 5
#endif
#define FIMC_IS_MAX_GFRAME (VIDEO_MAX_FRAME * 2) /* max shot buffer of F/W : 32, MAX 2 groups */
#define MIN_OF_ASYNC_SHOTS 1
#define MIN_OF_SYNC_SHOTS 2
#ifdef ENABLE_IS_CORE
#define MIN_OF_SHOT_RSC (MIN_OF_ASYNC_SHOTS + MIN_OF_SYNC_SHOTS)
#define MIN_OF_ASYNC_SHOTS_240FPS (MIN_OF_ASYNC_SHOTS + 2)
#else
#define MIN_OF_SHOT_RSC (1)
#define MIN_OF_ASYNC_SHOTS_240FPS (MIN_OF_ASYNC_SHOTS + 0)
#endif
#ifdef ENABLE_SYNC_REPROCESSING
#define REPROCESSING_TICK_COUNT (7) /* about 200ms */
#endif
enum fimc_is_group_state {
FIMC_IS_GROUP_OPEN,
FIMC_IS_GROUP_INIT,
FIMC_IS_GROUP_START,
FIMC_IS_GROUP_SHOT,
FIMC_IS_GROUP_REQUEST_FSTOP,
FIMC_IS_GROUP_FORCE_STOP,
FIMC_IS_GROUP_OTF_INPUT,
FIMC_IS_GROUP_OTF_OUTPUT,
FIMC_IS_GROUP_PIPE_INPUT,
FIMC_IS_GROUP_PIPE_OUTPUT,
FIMC_IS_GROUP_SEMI_PIPE_INPUT,
FIMC_IS_GROUP_SEMI_PIPE_OUTPUT
};
enum fimc_is_group_input_type {
GROUP_INPUT_MEMORY,
GROUP_INPUT_OTF,
GROUP_INPUT_PIPE,
GROUP_INPUT_SEMI_PIPE
};
struct fimc_is_frame;
struct fimc_is_device_ischain;
typedef int (*fimc_is_shot_callback)(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame);
typedef int (*fimc_is_pipe_shot_callback)(struct fimc_is_device_ischain *device,
struct fimc_is_group *group,
struct fimc_is_frame *frame);
struct fimc_is_group_frame {
struct list_head list;
u32 fcount;
struct fimc_is_crop canv;
struct camera2_node_group group_cfg[GROUP_SLOT_MAX];
};
struct fimc_is_group_framemgr {
struct fimc_is_group_frame gframe[FIMC_IS_MAX_GFRAME];
spinlock_t gframe_slock;
struct list_head gframe_head;
u32 gframe_cnt;
};
struct fimc_is_group {
struct fimc_is_group *next;
struct fimc_is_group *prev;
struct fimc_is_group *gnext;
struct fimc_is_group *gprev;
struct fimc_is_group *parent;
struct fimc_is_group *child;
struct fimc_is_group *head;
struct fimc_is_group *tail;
struct fimc_is_subdev leader;
struct fimc_is_subdev *junction;
struct fimc_is_subdev *subdev[ENTRY_END];
/* for otf interface */
atomic_t sensor_fcount;
atomic_t backup_fcount;
struct semaphore smp_trigger;
struct semaphore smp_shot;
atomic_t smp_shot_count;
u32 init_shots;
u32 asyn_shots;
u32 sync_shots;
u32 skip_shots;
#ifdef ENABLE_FAST_SHOT
struct camera2_ctl fast_ctl;
#endif
struct camera2_aa_ctl intent_ctl;
u32 id; /* group id */
u32 slot; /* group slot */
u32 instance; /* device instance */
u32 source_vid; /* source video id */
u32 pcount; /* program count */
u32 fcount; /* frame count */
atomic_t scount; /* shot count */
atomic_t rcount; /* request count */
unsigned long state;
struct list_head gframe_head;
u32 gframe_cnt;
fimc_is_shot_callback shot_callback;
fimc_is_pipe_shot_callback pipe_shot_callback;
struct fimc_is_device_ischain *device;
struct fimc_is_device_sensor *sensor;
enum fimc_is_device_type device_type;
#ifdef DEBUG_AA
#ifdef DEBUG_FLASH
enum aa_ae_flashmode flashmode;
struct camera2_flash_dm flash;
#endif
#endif
#ifdef MEASURE_TIME
#ifdef MONITOR_TIME
struct fimc_is_time time;
#endif
#endif
u32 aeflashMode; /* Flash Mode Control */
};
enum fimc_is_group_task_state {
FIMC_IS_GTASK_START,
FIMC_IS_GTASK_REQUEST_STOP
};
struct fimc_is_group_task {
u32 id;
struct task_struct *task;
struct kthread_worker worker;
struct semaphore smp_resource;
unsigned long state;
atomic_t refcount;
#ifdef ENABLE_SYNC_REPROCESSING
atomic_t rep_tick; /* Sync reprocessing tick */
#endif
};
struct fimc_is_groupmgr {
struct fimc_is_group_framemgr gframemgr[FIMC_IS_STREAM_COUNT];
struct fimc_is_group *leader[FIMC_IS_STREAM_COUNT];
struct fimc_is_group *group[FIMC_IS_STREAM_COUNT][GROUP_SLOT_MAX];
struct fimc_is_group_task gtask[GROUP_ID_MAX];
};
int fimc_is_groupmgr_probe(struct fimc_is_groupmgr *groupmgr);
int fimc_is_groupmgr_init(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_device_ischain *device);
int fimc_is_groupmgr_start(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_device_ischain *device);
int fimc_is_groupmgr_stop(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_device_ischain *device);
int fimc_is_group_probe(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group,
struct fimc_is_device_sensor *sensor,
struct fimc_is_device_ischain *device,
fimc_is_shot_callback shot_callback,
u32 slot,
u32 id,
char *name,
const struct fimc_is_subdev_ops *sops);
int fimc_is_group_open(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group, u32 id,
struct fimc_is_video_ctx *vctx);
int fimc_is_group_close(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group);
int fimc_is_group_init(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group,
u32 input_type,
u32 video_id,
u32 stream_leader);
int fimc_is_group_start(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group);
int fimc_is_group_stop(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group);
int fimc_is_group_buffer_queue(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group,
struct fimc_is_queue *queue,
u32 index);
int fimc_is_group_buffer_finish(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group, u32 index);
int fimc_is_group_shot(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group,
struct fimc_is_frame *frame);
int fimc_is_group_done(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group,
struct fimc_is_frame *frame,
u32 done_state);
int fimc_is_gframe_cancel(struct fimc_is_groupmgr *groupmgr,
struct fimc_is_group *group, u32 target_fcount);
unsigned long fimc_is_group_lock(struct fimc_is_group *group,
enum fimc_is_device_type device_type,
bool leader_lock);
void fimc_is_group_unlock(struct fimc_is_group *group, unsigned long flags,
enum fimc_is_device_type device_type,
bool leader_lock);
void fimc_is_group_subdev_cancel(struct fimc_is_group *group,
struct fimc_is_frame *ldr_frame,
enum fimc_is_device_type device_type,
enum fimc_is_frame_state frame_state,
bool flush);
/* get head group's framemgr */
#define GET_HEAD_GROUP_FRAMEMGR(group) \
(((group) && ((group)->head) && ((group)->head->leader.vctx)) ? (&((group)->head->leader).vctx->queue.framemgr) : NULL)
#endif

View file

@ -0,0 +1,124 @@
/*
* driver for FIMC-IS SPI
*
* Copyright (c) 2011, Samsung Electronics. All rights reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include <linux/module.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#endif
#include "fimc-is-i2c.h"
#include "fimc-is-core.h"
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
int fimc_is_i2c_s_pin(struct i2c_client *client, int state)
{
int ret = 0;
char pin_ctrl[10] = {0, };
struct device *i2c_dev = NULL;
struct pinctrl *pinctrl_i2c = NULL;
if (client == NULL) {
err("client is NULL.");
return ret;
}
switch (state) {
case I2C_PIN_STATE_ON:
sprintf(pin_ctrl, "on_i2c");
break;
case I2C_PIN_STATE_OFF:
sprintf(pin_ctrl, "off_i2c");
break;
case I2C_PIN_STATE_HOST:
sprintf(pin_ctrl, "i2c_host");
break;
case I2C_PIN_STATE_FW:
sprintf(pin_ctrl, "i2c_fw");
break;
case I2C_PIN_STATE_DEFAULT:
default:
sprintf(pin_ctrl, "default");
break;
}
i2c_dev = client->dev.parent->parent;
pinctrl_i2c = devm_pinctrl_get_select(i2c_dev, pin_ctrl);
if (IS_ERR_OR_NULL(pinctrl_i2c)) {
printk(KERN_ERR "%s: Failed to configure i2c pin\n", __func__);
} else {
devm_pinctrl_put(pinctrl_i2c);
}
return ret;
}
static int fimc_is_i2c0_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct fimc_is_core *core;
if (fimc_is_dev == NULL) {
warn("fimc_is_dev is not yet probed(i2c0)");
return -EPROBE_DEFER;
}
core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev);
if (!core)
panic("core is NULL");
core->client0 = client;
pr_info("%s %s: fimc_is_i2c0 driver probed!\n",
dev_driver_string(&client->dev), dev_name(&client->dev));
return 0;
}
static int fimc_is_i2c0_remove(struct i2c_client *client)
{
return 0;
}
#ifdef CONFIG_OF
static struct of_device_id fimc_is_i2c0_dt_ids[] = {
{ .compatible = "samsung,fimc_is_i2c0",},
{},
};
MODULE_DEVICE_TABLE(of, fimc_is_i2c0_dt_ids);
#endif
static const struct i2c_device_id fimc_is_i2c0_id[] = {
{"fimc_is_i2c0", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, fimc_is_i2c0_id);
static struct i2c_driver fimc_is_i2c0_driver = {
.driver = {
.name = "fimc_is_i2c0",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = fimc_is_i2c0_dt_ids,
#endif
},
.probe = fimc_is_i2c0_probe,
.remove = fimc_is_i2c0_remove,
.id_table = fimc_is_i2c0_id,
};
module_i2c_driver(fimc_is_i2c0_driver);
#endif /* CONFIG_I2C || CONFIG_I2C_MODULE */
MODULE_DESCRIPTION("FIMC-IS I2C driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,292 @@
/*
* Samsung Exynos SoC series FIMC-IS2 driver
*
* exynos fimc-is2 device interface functions
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "fimc-is-interface-wrap.h"
int fimc_is_itf_s_param_wrap(struct fimc_is_device_ischain *device,
u32 lindex, u32 hindex, u32 indexes)
{
int ret = 0;
if (lindex || hindex) {
ret = fimc_is_hw_s_param(device->interface, device->instance,
lindex, hindex, indexes);
}
return ret;
}
int fimc_is_itf_a_param_wrap(struct fimc_is_device_ischain *device, u32 group)
{
int ret = 0;
u32 setfile;
setfile = (device->setfile & FIMC_IS_SETFILE_MASK);
ret = fimc_is_hw_a_param(device->interface,
device->instance, group, setfile);
return ret;
}
int fimc_is_itf_f_param_wrap(struct fimc_is_device_ischain *device, u32 group)
{
int ret = 0;
u32 setfile;
setfile = (device->setfile & FIMC_IS_SETFILE_MASK);
ret = fimc_is_hw_a_param(device->interface,
device->instance, (group & GROUP_ID_PARM_MASK), setfile);
return ret;
}
int fimc_is_itf_enum_wrap(struct fimc_is_device_ischain *device)
{
int ret = 0;
mdbgd_ischain("%s()\n", device, __func__);
ret = fimc_is_hw_enum(device->interface);
if (ret) {
merr("fimc_is_hw_enum is fail(%d)", device, ret);
CALL_POPS(device, print_clk, device->pdev);
}
return ret;
}
void fimc_is_itf_storefirm_wrap(struct fimc_is_device_ischain *device)
{
mdbgd_ischain("%s()\n", device, __func__);
fimc_is_storefirm(device->interface);
}
void fimc_is_itf_restorefirm_wrap(struct fimc_is_device_ischain *device)
{
mdbgd_ischain("%s()\n", device, __func__);
fimc_is_restorefirm(device->interface);
}
int fimc_is_itf_set_fwboot_wrap(struct fimc_is_device_ischain *device, u32 val)
{
int ret = 0;
mdbgd_ischain("%s()\n", device, __func__);
ret = fimc_is_set_fwboot(device->interface, val);
if (ret) {
merr("fimc_is_set_fwboot is fail(%d)", device, ret);
}
return ret;
}
int fimc_is_itf_open_wrap(struct fimc_is_device_ischain *device, u32 module_id,
u32 flag, u32 offset_path)
{
int ret = 0;
ret = fimc_is_hw_open(device->interface,
device->instance,
module_id,
device->dvaddr_shared,
device->dvaddr_shared + (offset_path * 4),
flag,
&device->margin_width,
&device->margin_height);
if (ret) {
merr("fimc_is_hw_open is fail", device);
CALL_POPS(device, print_clk, device->pdev);
fimc_is_sensor_gpio_dbg(device->sensor);
ret = -EINVAL;
}
return ret;
}
int fimc_is_itf_close_wrap(struct fimc_is_device_ischain *device)
{
int ret = 0;
ret = fimc_is_hw_close(device->interface, device->instance);
if (ret)
merr("fimc_is_hw_close is fail", device);
return ret;
}
int fimc_is_itf_setaddr_wrap(struct fimc_is_interface *itf,
struct fimc_is_device_ischain *device, ulong *setfile_addr)
{
int ret = 0;
mdbgd_ischain("%s\n", device, __func__);
ret = fimc_is_hw_saddr(itf, device->instance, (u32 *)setfile_addr);
if (ret)
merr("fimc_is_hw_saddr is fail(%d)", device, ret);
return ret;
}
int fimc_is_itf_setfile_wrap(struct fimc_is_interface *itf, ulong setfile_addr,
struct fimc_is_device_ischain *device)
{
int ret = 0;
mdbgd_ischain("%s\n", device, __func__);
ret = fimc_is_hw_setfile(itf, device->instance);
if (ret)
merr("fimc_is_hw_setfile is fail(%d)", device, ret);
return ret;
}
int fimc_is_itf_map_wrap(struct fimc_is_device_ischain *device,
u32 group, u32 shot_addr, u32 shot_size)
{
int ret = 0;
mdbgd_ischain("%s()\n", device, __func__);
ret = fimc_is_hw_map(device->interface, device->instance,
group, shot_addr, shot_size);
if (ret)
merr("fimc_is_hw_map is fail(%d)", device, ret);
return ret;
}
int fimc_is_itf_unmap_wrap(struct fimc_is_device_ischain *device, u32 group)
{
int ret = 0;
mdbgd_ischain("%s()\n", device, __func__);
ret = fimc_is_hw_unmap(device->interface, device->instance, group);
if (ret)
merr("fimc_is_hw_unmap is fail(%d)", device, ret);
return ret;
}
int fimc_is_itf_stream_on_wrap(struct fimc_is_device_ischain *device)
{
int ret = 0;
mdbgd_ischain("%s()\n", device, __func__);
ret = fimc_is_hw_stream_on(device->interface, device->instance);
if (ret) {
merr("fimc_is_hw_stream_on is fail(%d)", device, ret);
CALL_POPS(device, print_clk, device->pdev);
fimc_is_sensor_gpio_dbg(device->sensor);
}
return ret;
}
int fimc_is_itf_stream_off_wrap(struct fimc_is_device_ischain *device)
{
int ret = 0;
mdbgd_ischain("%s()\n", device, __func__);
ret = fimc_is_hw_stream_off(device->interface, device->instance);
if (ret) {
merr("fimc_is_hw_stream_off is fail(%d)", device, ret);
CALL_POPS(device, print_clk, device->pdev);
if (!device->sensor) {
merr("sensor is NULL", device);
ret = -ENODEV;
goto p_err;
}
fimc_is_sensor_gpio_dbg(device->sensor);
}
p_err:
return ret;
}
int fimc_is_itf_process_on_wrap(struct fimc_is_device_ischain *device, u32 group)
{
int ret = 0;
ret = fimc_is_hw_process_on(device->interface, device->instance, group);
return ret;
}
int fimc_is_itf_process_off_wrap(struct fimc_is_device_ischain *device, u32 group,
u32 fstop)
{
int ret = 0;
ret = fimc_is_hw_process_off(device->interface, device->instance,
group, fstop);
return ret;
}
int fimc_is_itf_power_down_wrap(struct fimc_is_interface *interface, u32 instance)
{
int ret = 0;
ret = fimc_is_hw_power_down(interface, instance);
return ret;
}
int fimc_is_itf_sys_ctl_wrap(struct fimc_is_device_ischain *device,
int cmd, int val)
{
int ret = 0;
ret = fimc_is_hw_sys_ctl(device->interface, device->instance,
cmd, val);
return ret;
}
int fimc_is_itf_sensor_mode_wrap(struct fimc_is_device_ischain *ischain,
struct fimc_is_sensor_cfg *cfg)
{
int ret = 0;
ret = fimc_is_hw_sensor_mode(ischain->interface, ischain->instance,
((cfg->mode << 16) | (ischain->module & 0xFFFF)));
if (ret)
merr("fimc_is_hw_sensor_mode is fail(%d)", ischain, ret);
return ret;
}
int fimc_is_itf_shot_wrap(struct fimc_is_device_ischain *device,
struct fimc_is_group *group, struct fimc_is_frame *frame)
{
int ret = 0;
ret = fimc_is_hw_shot_nblk(device->interface,
device->instance,
GROUP_ID(group->id),
frame->dvaddr_shot,
frame->fcount,
frame->rcount);
return ret;
}

View file

@ -0,0 +1,558 @@
/*
* Samsung Exynos SoC series FIMC-IS2 driver
*
* exynos fimc-is2 device interface functions
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "fimc-is-interface-wrap.h"
#include "fimc-is-interface-library.h"
#include "fimc-is-param.h"
int fimc_is_itf_s_param_wrap(struct fimc_is_device_ischain *device,
u32 lindex, u32 hindex, u32 indexes)
{
struct fimc_is_hardware *hardware = NULL;
u32 instance = 0;
int ret = 0;
dbg_hw("%s\n", __func__);
hardware = device->hardware;
instance = device->instance;
ret = fimc_is_hardware_set_param(hardware, instance, device->is_region,
lindex, hindex, hardware->hw_map[instance]);
if (ret) {
merr("fimc_is_hardware_set_param is fail(%d)", device, ret);
return ret;
}
return ret;
}
int fimc_is_itf_a_param_wrap(struct fimc_is_device_ischain *device, u32 group)
{
struct fimc_is_hardware *hardware = NULL;
u32 instance = 0;
int ret = 0;
dbg_hw("%s\n", __func__);
hardware = device->hardware;
instance = device->instance;
#if !defined(DISABLE_SETFILE)
ret = fimc_is_hardware_apply_setfile(hardware, instance,
device->setfile & FIMC_IS_SETFILE_MASK,
hardware->hw_map[instance]);
if (ret) {
merr("fimc_is_hardware_apply_setfile is fail(%d)", device, ret);
return ret;
}
#endif
return ret;
}
int fimc_is_itf_f_param_wrap(struct fimc_is_device_ischain *device, u32 group)
{
struct fimc_is_hardware *hardware= NULL;
u32 instance = 0;
int ret = 0;
dbg_hw("%s\n", __func__);
hardware = device->hardware;
instance = device->instance;
return ret;
}
int fimc_is_itf_enum_wrap(struct fimc_is_device_ischain *device)
{
int ret = 0;
dbg_hw("%s\n", __func__);
return ret;
}
void fimc_is_itf_storefirm_wrap(struct fimc_is_device_ischain *device)
{
dbg_hw("%s\n", __func__);
return;
}
void fimc_is_itf_restorefirm_wrap(struct fimc_is_device_ischain *device)
{
dbg_hw("%s\n", __func__);
return;
}
int fimc_is_itf_set_fwboot_wrap(struct fimc_is_device_ischain *device, u32 val)
{
int ret = 0;
dbg_hw("%s\n", __func__);
return ret;
}
int fimc_is_itf_open_wrap(struct fimc_is_device_ischain *device, u32 module_id,
u32 flag, u32 offset_path)
{
struct fimc_is_hardware *hardware;
struct fimc_is_path_info *path;
struct fimc_is_group *group;
struct is_region *region;
struct fimc_is_device_sensor *sensor;
u32 instance = 0;
u32 hw_id = 0;
u32 group_slot = -1;
u32 group_id = -1;
int ret = 0;
int hw_list[GROUP_HW_MAX];
int hw_index;
int hw_maxnum = 0;
u32 rsccount;
info_hw("%s: offset_path(0x%8x) flag(%d) sen(%d)\n", __func__, offset_path, flag, module_id);
sensor = device->sensor;
instance = device->instance;
hardware = device->hardware;
path = (struct fimc_is_path_info *)&device->is_region->shared[offset_path];
rsccount = atomic_read(&hardware->rsccount);
region = device->is_region;
region->shared[MAX_SHARED_COUNT-1] = MAGIC_NUMBER;
for (hw_id = 0; hw_id < DEV_HW_END; hw_id++)
clear_bit(hw_id, &hardware->hw_map[instance]);
group_slot = GROUP_SLOT_3AA;
group = &device->group_3aa;
group_id = path->group[group_slot];
dbg_hw("itf_open_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_open(hardware, hw_id, group, instance,
flag, module_id);
if (ret) {
err("fimc_is_hardware_open(%d) is fail", hw_id);
return ret;
}
}
group_slot = GROUP_SLOT_ISP;
group = &device->group_isp;
group_id = path->group[group_slot];
dbg_hw("itf_open_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_open(hardware, hw_id, group, instance,
flag, module_id);
if (ret) {
err("fimc_is_hardware_open(%d) is fail", hw_id);
return ret;
}
}
group_slot = GROUP_SLOT_DIS;
group = &device->group_dis;
group_id = path->group[group_slot];
dbg_hw("itf_open_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_open(hardware, hw_id, group, instance,
flag, module_id);
if (ret) {
err("fimc_is_hardware_open(%d) is fail", hw_id);
return ret;
}
}
group_slot = GROUP_SLOT_MCS;
group = &device->group_mcs;
group_id = path->group[group_slot];
dbg_hw("itf_open_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_open(hardware, hw_id, group, instance,
flag, module_id);
if (ret) {
err("fimc_is_hardware_open(%d) is fail", hw_id);
return ret;
}
}
group_slot = GROUP_SLOT_VRA;
group = &device->group_vra;
group_id = path->group[group_slot];
dbg_hw("itf_open_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_open(hardware, hw_id, group, instance,
flag, module_id);
if (ret) {
err("fimc_is_hardware_open(%d) is fail", hw_id);
return ret;
}
}
hardware->sensor_position[instance] = sensor->position;
atomic_inc(&hardware->rsccount);
info("%s: done: hw_map[0x%lx][RSC:%d][%d]\n", __func__,
hardware->hw_map[instance], atomic_read(&hardware->rsccount),
hardware->sensor_position[instance]);
return ret;
}
int fimc_is_itf_close_wrap(struct fimc_is_device_ischain *device)
{
struct fimc_is_hardware *hardware;
struct fimc_is_path_info *path;
u32 offset_path = 0;
u32 instance = 0;
u32 hw_id = 0;
u32 group_slot = -1;
u32 group_id = -1;
int ret = 0;
int hw_list[GROUP_HW_MAX];
int hw_index;
int hw_maxnum = 0;
u32 rsccount;
dbg_hw("%s\n", __func__);
hardware = device->hardware;
instance = device->instance;
offset_path = (sizeof(struct sensor_open_extended) / 4) + 1;
path = (struct fimc_is_path_info *)&device->is_region->shared[offset_path];
rsccount = atomic_read(&hardware->rsccount);
if (rsccount == 1)
fimc_is_flush_ddk_thread();
#if !defined(DISABLE_SETFILE)
ret = fimc_is_hardware_delete_setfile(hardware, instance,
hardware->hw_map[instance]);
if (ret) {
merr("fimc_is_hardware_delete_setfile is fail(%d)", device, ret);
return ret;
}
#endif
group_slot = GROUP_SLOT_3AA;
group_id = path->group[group_slot];
dbg_hw("itf_close_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_close(hardware, hw_id, instance);
if (ret) {
err("fimc_is_hardware_close(%d) is fail", hw_id);
return ret;
}
}
group_slot = GROUP_SLOT_ISP;
group_id = path->group[group_slot];
dbg_hw("itf_close_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_close(hardware, hw_id, instance);
if (ret) {
err("fimc_is_hardware_close(%d) is fail", hw_id);
return ret;
}
}
group_slot = GROUP_SLOT_DIS;
group_id = path->group[group_slot];
dbg_hw("itf_close_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_close(hardware, hw_id, instance);
if (ret) {
err("fimc_is_hardware_close(%d) is fail", hw_id);
return ret;
}
}
group_slot = GROUP_SLOT_MCS;
group_id = path->group[group_slot];
dbg_hw("itf_close_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_close(hardware, hw_id, instance);
if (ret) {
err("fimc_is_hardware_close(%d) is fail", hw_id);
return ret;
}
}
group_slot = GROUP_SLOT_VRA;
group_id = path->group[group_slot];
dbg_hw("itf_close_wrap: group[SLOT_%d]=[%x]\n", group_slot, group_id);
hw_maxnum = fimc_is_get_hw_list(group_id, hw_list);
for (hw_index = 0; hw_index < hw_maxnum; hw_index++) {
hw_id = hw_list[hw_index];
ret = fimc_is_hardware_close(hardware, hw_id, instance);
if (ret) {
err("fimc_is_hardware_close(%d) is fail", hw_id);
return ret;
}
}
atomic_dec(&hardware->rsccount);
if (rsccount == 1)
check_lib_memory_leak();
info("%s: done: hw_map[0x%lx][RSC:%d]\n", __func__,
hardware->hw_map[instance], atomic_read(&hardware->rsccount));
return ret;
}
int fimc_is_itf_setaddr_wrap(struct fimc_is_interface *itf,
struct fimc_is_device_ischain *device, ulong *setfile_addr)
{
int ret = 0;
dbg_hw("%s\n", __func__);
*setfile_addr = device->resourcemgr->minfo.kvaddr_setfile;
return ret;
}
int fimc_is_itf_setfile_wrap(struct fimc_is_interface *itf, ulong setfile_addr,
struct fimc_is_device_ischain *device)
{
struct fimc_is_hardware *hardware;
u32 instance = 0;
int ret = 0;
dbg_hw("%s\n", __func__);
hardware = device->hardware;
instance = device->instance;
#if !defined(DISABLE_SETFILE)
ret = fimc_is_hardware_load_setfile(hardware, setfile_addr, instance,
hardware->hw_map[instance]);
if (ret) {
merr("fimc_is_hardware_load_setfile is fail(%d)", device, ret);
return ret;
}
#endif
return ret;
}
int fimc_is_itf_map_wrap(struct fimc_is_device_ischain *device,
u32 group, u32 shot_addr, u32 shot_size)
{
int ret = 0;
dbg_hw("%s\n", __func__);
return ret;
}
int fimc_is_itf_unmap_wrap(struct fimc_is_device_ischain *device, u32 group)
{
int ret = 0;
dbg_hw("%s\n", __func__);
return ret;
}
int fimc_is_itf_stream_on_wrap(struct fimc_is_device_ischain *device)
{
struct fimc_is_hardware *hardware;
u32 instance;
ulong hw_map;
int ret = 0;
dbg_hw("%s\n", __func__);
hardware = device->hardware;
instance = device->instance;
hw_map = hardware->hw_map[instance];
ret = fimc_is_hardware_sensor_start(hardware, instance, hw_map);
if (ret) {
merr("fimc_is_hardware_stream_on is fail(%d)", device, ret);
return ret;
}
return ret;
}
int fimc_is_itf_stream_off_wrap(struct fimc_is_device_ischain *device)
{
struct fimc_is_hardware *hardware;
u32 instance;
ulong hw_map;
int ret = 0;
dbg_hw("%s\n", __func__);
hardware = device->hardware;
instance = device->instance;
hw_map = hardware->hw_map[instance];
ret = fimc_is_hardware_sensor_stop(hardware, instance, hw_map);
if (ret) {
merr("fimc_is_hardware_stream_off is fail(%d)", device, ret);
return ret;
}
return ret;
}
int fimc_is_itf_process_on_wrap(struct fimc_is_device_ischain *device, u32 group)
{
struct fimc_is_hardware *hardware;
u32 instance = 0;
u32 group_id;
int ret = 0;
hardware = device->hardware;
instance = device->instance;
info_hw("[%d]itf_process_on_wrap: [G:0x%x]\n", instance, group);
for (group_id = GROUP_ID_3AA0; group_id < GROUP_ID_MAX; group_id++) {
if (((group) & GROUP_ID(group_id)) &&
(GET_DEVICE_TYPE_BY_GRP(group_id) == FIMC_IS_DEVICE_ISCHAIN)) {
ret = fimc_is_hardware_process_start(hardware, instance, group_id);
if (ret) {
merr("fimc_is_hardware_process_start is fail(%d)", device, ret);
return ret;
}
}
}
return ret;
}
int fimc_is_itf_process_off_wrap(struct fimc_is_device_ischain *device, u32 group,
u32 fstop)
{
struct fimc_is_hardware *hardware;
u32 instance = 0;
u32 group_id;
int ret = 0;
hardware = device->hardware;
instance = device->instance;
info_hw("[%d]itf_process_off_wrap: [G:0x%x](%d)\n", instance, group, fstop);
for (group_id = GROUP_ID_3AA0; group_id < GROUP_ID_MAX; group_id++) {
if (((group) & GROUP_ID(group_id)) &&
(GET_DEVICE_TYPE_BY_GRP(group_id) == FIMC_IS_DEVICE_ISCHAIN))
fimc_is_hardware_process_stop(hardware, instance, group_id, fstop);
}
return ret;
}
int fimc_is_itf_power_down_wrap(struct fimc_is_interface *interface, u32 instance)
{
int ret = 0;
dbg_hw("%s\n", __func__);
return ret;
}
int fimc_is_itf_sys_ctl_wrap(struct fimc_is_device_ischain *device,
int cmd, int val)
{
int ret = 0;
dbg_hw("%s\n", __func__);
return ret;
}
int fimc_is_itf_sensor_mode_wrap(struct fimc_is_device_ischain *ischain,
struct fimc_is_sensor_cfg *cfg)
{
dbg_hw("%s\n", __func__);
return 0;
}
void fimc_is_itf_fwboot_init(struct fimc_is_interface *this)
{
clear_bit(IS_IF_LAUNCH_FIRST, &this->launch_state);
clear_bit(IS_IF_LAUNCH_SUCCESS, &this->launch_state);
clear_bit(IS_IF_RESUME, &this->fw_boot);
clear_bit(IS_IF_SUSPEND, &this->fw_boot);
this->fw_boot_mode = COLD_BOOT;
}
int fimc_is_itf_shot_wrap(struct fimc_is_device_ischain *device,
struct fimc_is_group *group, struct fimc_is_frame *frame)
{
struct fimc_is_hardware *hardware;
struct fimc_is_interface *itf;
u32 instance = 0;
int ret = 0;
ulong flags;
hardware = device->hardware;
instance = device->instance;
itf = device->interface;
if ((test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)
&& !atomic_read(&hardware->streaming[hardware->sensor_position[instance]]))
|| (!test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state) && !atomic_read(&group->scount))) {
#if !defined(DISABLE_SETFILE)
ret = fimc_is_hardware_apply_setfile(hardware, instance,
device->setfile & FIMC_IS_SETFILE_MASK,
hardware->hw_map[instance]);
if (ret) {
merr("fimc_is_hardware_apply_setfile is fail(%d)", device, ret);
return ret;
}
#endif
}
ret = fimc_is_hardware_grp_shot(hardware, instance, group, frame,
hardware->hw_map[instance]);
if (ret) {
merr("fimc_is_hardware_grp_shot is fail(%d)", device, ret);
return ret;
}
spin_lock_irqsave(&itf->shot_check_lock, flags);
atomic_set(&itf->shot_check[instance], 1);
spin_unlock_irqrestore(&itf->shot_check_lock, flags);
return ret;
}

View file

@ -0,0 +1,51 @@
/*
* Samsung Exynos SoC series FIMC-IS2 driver
*
* exynos fimc-is2 device interface functions
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_DEVICE_ITF_H
#define FIMC_IS_DEVICE_ITF_H
#include "fimc-is-core.h"
#include "fimc-is-groupmgr.h"
#include "fimc-is-device-ischain.h"
int fimc_is_itf_s_param_wrap(struct fimc_is_device_ischain *device,
u32 lindex, u32 hindex, u32 indexes);
int fimc_is_itf_a_param_wrap(struct fimc_is_device_ischain *device, u32 group);
int fimc_is_itf_f_param_wrap(struct fimc_is_device_ischain *device, u32 group);
int fimc_is_itf_enum_wrap(struct fimc_is_device_ischain *device);
void fimc_is_itf_storefirm_wrap(struct fimc_is_device_ischain *device);
void fimc_is_itf_restorefirm_wrap(struct fimc_is_device_ischain *device);
int fimc_is_itf_set_fwboot_wrap(struct fimc_is_device_ischain *device, u32 val);
int fimc_is_itf_open_wrap(struct fimc_is_device_ischain *device, u32 module_id,
u32 flag, u32 offset_path);
int fimc_is_itf_close_wrap(struct fimc_is_device_ischain *device);
int fimc_is_itf_setaddr_wrap(struct fimc_is_interface *itf,
struct fimc_is_device_ischain *device, ulong *setfile_addr);
int fimc_is_itf_setfile_wrap(struct fimc_is_interface *itf, ulong setfile_addr,
struct fimc_is_device_ischain *device);
int fimc_is_itf_map_wrap(struct fimc_is_device_ischain *device,
u32 group, u32 shot_addr, u32 shot_size);
int fimc_is_itf_unmap_wrap(struct fimc_is_device_ischain *device, u32 group);
int fimc_is_itf_stream_on_wrap(struct fimc_is_device_ischain *device);
int fimc_is_itf_stream_off_wrap(struct fimc_is_device_ischain *device);
int fimc_is_itf_process_on_wrap(struct fimc_is_device_ischain *device, u32 group);
int fimc_is_itf_process_off_wrap(struct fimc_is_device_ischain *device, u32 group,
u32 fstop);
int fimc_is_itf_power_down_wrap(struct fimc_is_interface *interface, u32 instance);
int fimc_is_itf_sys_ctl_wrap(struct fimc_is_device_ischain *device,
int cmd, int val);
int fimc_is_itf_sensor_mode_wrap(struct fimc_is_device_ischain *ischain,
struct fimc_is_sensor_cfg *cfg);
int fimc_is_itf_shot_wrap(struct fimc_is_device_ischain *device,
struct fimc_is_group *group, struct fimc_is_frame *frame);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,294 @@
/*
* drivers/media/video/exynos/fimc-is-mc2/fimc-is-interface.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* The header file related to camera
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_INTERFACE_H
#define FIMC_IS_INTERFACE_H
#include "fimc-is-metadata.h"
#include "fimc-is-framemgr.h"
#include "fimc-is-video.h"
#include "fimc-is-time.h"
#include "fimc-is-cmd.h"
/*#define TRACE_WORK*/
/* cam_ctrl : 1
shot : 2 */
#define TRACE_WORK_ID_CAMCTRL 0x00001
#define TRACE_WORK_ID_GENERAL 0x00002
#define TRACE_WORK_ID_SHOT 0x00004
#define TRACE_WORK_ID_30C 0x00008
#define TRACE_WORK_ID_30P 0x00010
#define TRACE_WORK_ID_31C 0x00020
#define TRACE_WORK_ID_31P 0x00040
#define TRACE_WORK_ID_I0C 0x00080
#define TRACE_WORK_ID_I0P 0x00100
#define TRACE_WORK_ID_I1C 0x00200
#define TRACE_WORK_ID_I1P 0x00400
#define TRACE_WORK_ID_SCC 0x00800
#define TRACE_WORK_ID_SCP 0x01000
#define TRACE_WORK_ID_M0P 0x02000
#define TRACE_WORK_ID_M1P 0x04000
#define TRACE_WORK_ID_M2P 0x08000
#define TRACE_WORK_ID_M3P 0x10000
#define TRACE_WORK_ID_M4P 0x20000
#define TRACE_WORK_ID_MASK 0x3FFFF
#define MAX_NBLOCKING_COUNT 3
#define MAX_WORK_COUNT 10
#define TRY_TIMEOUT_COUNT 2
#define SENSOR_TIMEOUT_COUNT 2
#define TRY_RECV_AWARE_COUNT 10000
#define LOWBIT_OF(num) (num >= 32 ? 0 : (u32)1<<num)
#define HIGHBIT_OF(num) (num >= 32 ? (u32)1<<(num-32) : 0)
enum fimc_is_interface_state {
IS_IF_STATE_OPEN,
IS_IF_STATE_READY,
IS_IF_STATE_START,
IS_IF_STATE_BUSY,
IS_IF_STATE_LOGGING
};
enum interrupt_map {
INTR_GENERAL,
INTR_SHOT_DONE,
INTR_30C_FDONE,
INTR_30P_FDONE,
INTR_31C_FDONE,
INTR_31P_FDONE,
INTR_I0X_FDONE,
INTR_I1X_FDONE,
INTR_SCX_FDONE,
INTR_M0X_FDONE,
INTR_M1X_FDONE,
INTR_MAX_MAP
};
enum work_map {
WORK_GENERAL,
WORK_SHOT_DONE,
WORK_30C_FDONE,
WORK_30P_FDONE,
WORK_31C_FDONE,
WORK_31P_FDONE,
WORK_I0C_FDONE,
WORK_I0P_FDONE,
WORK_I1C_FDONE,
WORK_I1P_FDONE,
WORK_SCC_FDONE,
WORK_SCP_FDONE,
WORK_M0P_FDONE,
WORK_M1P_FDONE,
WORK_M2P_FDONE,
WORK_M3P_FDONE,
WORK_M4P_FDONE,
WORK_MAX_MAP
};
enum streaming_state {
IS_IF_STREAMING_INIT,
IS_IF_STREAMING_OFF,
IS_IF_STREAMING_ON
};
enum processing_state {
IS_IF_PROCESSING_INIT,
IS_IF_PROCESSING_OFF,
IS_IF_PROCESSING_ON
};
enum pdown_ready_state {
IS_IF_POWER_DOWN_READY,
IS_IF_POWER_DOWN_NREADY
};
enum launch_state {
IS_IF_LAUNCH_FIRST,
IS_IF_LAUNCH_SUCCESS,
};
enum fw_boot_state {
IS_IF_RESUME,
IS_IF_SUSPEND,
};
enum fimc_is_fw_boot {
FIRST_LAUNCHING,
WARM_BOOT,
COLD_BOOT,
};
struct fimc_is_msg {
u32 id;
u32 command;
u32 instance;
u32 group;
u32 param1;
u32 param2;
u32 param3;
u32 param4;
};
struct fimc_is_work {
struct list_head list;
struct fimc_is_msg msg;
u32 fcount;
struct fimc_is_frame *frame;
};
struct fimc_is_work_list {
u32 id;
struct fimc_is_work work[MAX_WORK_COUNT];
spinlock_t slock_free;
spinlock_t slock_request;
struct list_head work_free_head;
u32 work_free_cnt;
struct list_head work_request_head;
u32 work_request_cnt;
wait_queue_head_t wait_queue;
};
struct fimc_is_interface {
void __iomem *regs;
struct is_common_reg __iomem *com_regs;
unsigned long state;
spinlock_t process_barrier;
struct fimc_is_msg process_msg;
struct mutex request_barrier;
struct fimc_is_msg request_msg;
atomic_t lock_pid;
wait_queue_head_t lock_wait_queue;
wait_queue_head_t init_wait_queue;
wait_queue_head_t idle_wait_queue;
struct fimc_is_msg reply;
#ifdef MEASURE_TIME
#ifdef INTERFACE_TIME
struct fimc_is_interface_time time[HIC_COMMAND_END];
#endif
#endif
struct workqueue_struct *workqueue;
struct work_struct work_wq[WORK_MAX_MAP];
struct fimc_is_work_list work_list[WORK_MAX_MAP];
/* sensor streaming flag */
enum streaming_state streaming[FIMC_IS_STREAM_COUNT];
/* firmware processing flag */
enum processing_state processing[FIMC_IS_STREAM_COUNT];
/* frrmware power down ready flag */
enum pdown_ready_state pdown_ready;
unsigned long fw_boot;
struct fimc_is_framemgr *framemgr;
struct fimc_is_work_list nblk_cam_ctrl;
/* shot timeout check */
spinlock_t shot_check_lock;
atomic_t shot_check[FIMC_IS_STREAM_COUNT];
atomic_t shot_timeout[FIMC_IS_STREAM_COUNT];
/* sensor timeout check */
atomic_t sensor_check[FIMC_IS_SENSOR_COUNT];
atomic_t sensor_timeout[FIMC_IS_SENSOR_COUNT];
struct timer_list timer;
/* callback func to handle error report for specific purpose */
void *err_report_data;
int (*err_report_vendor)(void *data, u32 err_report_type);
struct camera2_uctl isp_peri_ctl;
/* check firsttime */
unsigned long launch_state;
enum fimc_is_fw_boot fw_boot_mode;
ulong itf_kvaddr;
void *core;
};
void fimc_is_itf_fwboot_init(struct fimc_is_interface *this);
int fimc_is_interface_probe(struct fimc_is_interface *this,
struct fimc_is_minfo *minfo,
ulong regs,
u32 irq,
void *core_data);
int fimc_is_interface_open(struct fimc_is_interface *this);
int fimc_is_interface_close(struct fimc_is_interface *this);
void fimc_is_interface_lock(struct fimc_is_interface *this);
void fimc_is_interface_unlock(struct fimc_is_interface *this);
void fimc_is_interface_reset(struct fimc_is_interface *this);
void fimc_is_storefirm(struct fimc_is_interface *this);
void fimc_is_restorefirm(struct fimc_is_interface *this);
int fimc_is_set_fwboot(struct fimc_is_interface *this, int val);
/*for debugging*/
int print_fre_work_list(struct fimc_is_work_list *this);
int print_req_work_list(struct fimc_is_work_list *this);
int fimc_is_hw_logdump(struct fimc_is_interface *this);
int fimc_is_hw_regdump(struct fimc_is_interface *this);
int fimc_is_hw_memdump(struct fimc_is_interface *this,
ulong start,
ulong end);
int fimc_is_hw_enum(struct fimc_is_interface *this);
int fimc_is_hw_fault(struct fimc_is_interface *this);
int fimc_is_hw_open(struct fimc_is_interface *this,
u32 instance, u32 module, u32 info, u32 path, u32 flag,
u32 *mwidth, u32 *mheight);
int fimc_is_hw_close(struct fimc_is_interface *this,
u32 instance);
int fimc_is_hw_saddr(struct fimc_is_interface *interface,
u32 instance, u32 *setfile_addr);
int fimc_is_hw_setfile(struct fimc_is_interface *interface,
u32 instance);
int fimc_is_hw_process_on(struct fimc_is_interface *this,
u32 instance, u32 group);
int fimc_is_hw_process_off(struct fimc_is_interface *this,
u32 instance, u32 group, u32 mode);
int fimc_is_hw_stream_on(struct fimc_is_interface *interface,
u32 instance);
int fimc_is_hw_stream_off(struct fimc_is_interface *interface,
u32 instance);
int fimc_is_hw_s_param(struct fimc_is_interface *interface,
u32 instance, u32 lindex, u32 hindex, u32 indexes);
int fimc_is_hw_a_param(struct fimc_is_interface *this,
u32 instance, u32 group, u32 sub_mode);
int fimc_is_hw_g_capability(struct fimc_is_interface *this,
u32 instance, u32 address);
int fimc_is_hw_map(struct fimc_is_interface *this,
u32 instance, u32 group, u32 address, u32 size);
int fimc_is_hw_unmap(struct fimc_is_interface *this,
u32 instance, u32 group);
int fimc_is_hw_power_down(struct fimc_is_interface *interface,
u32 instance);
int fimc_is_hw_i2c_lock(struct fimc_is_interface *interface,
u32 instance, int clk, bool lock);
int fimc_is_hw_sys_ctl(struct fimc_is_interface *this,
u32 instance, int cmd, int val);
int fimc_is_hw_sensor_mode(struct fimc_is_interface *this,
u32 instance, int cfg);
int fimc_is_hw_shot_nblk(struct fimc_is_interface *this,
u32 instance, u32 group, u32 shot, u32 fcount, u32 rcount);
int fimc_is_hw_s_camctrl_nblk(struct fimc_is_interface *this,
u32 instance, u32 address, u32 fcount);
int fimc_is_hw_msg_test(struct fimc_is_interface *this, u32 sync_id, u32 msg_test_id);
/* func to register error report callback */
int fimc_is_set_err_report_vendor(struct fimc_is_interface *itf,
void *err_report_data,
int (*err_report_vendor)(void *data, u32 err_report_type));
#endif

View file

@ -0,0 +1,345 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is core functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
#include <linux/exynos_iovmm.h>
#else
#include <plat/iovmm.h>
#endif
#if defined(CONFIG_VIDEOBUF2_ION)
/* fimc-is vb2 buffer operations */
static inline ulong fimc_is_vb2_ion_plane_kvaddr(
struct fimc_is_vb2_buf *vbuf, u32 plane)
{
return (ulong)vb2_plane_vaddr(&vbuf->vb, plane);
}
static inline ulong fimc_is_vb2_ion_plane_cookie(
struct fimc_is_vb2_buf *vbuf, u32 plane)
{
return (ulong)vb2_plane_cookie(&vbuf->vb, plane);
}
static dma_addr_t fimc_is_vb2_ion_plane_dvaddr(
struct fimc_is_vb2_buf *vbuf, u32 plane)
{
dma_addr_t dva = 0;
WARN_ON(vb2_ion_dma_address(vb2_plane_cookie(&vbuf->vb, plane), &dva) != 0);
return (ulong)dva;
}
static void fimc_is_vb2_ion_plane_prepare(struct fimc_is_vb2_buf *vbuf,
u32 plane, bool exact)
{
struct vb2_buffer *vb = &vbuf->vb;
enum dma_data_direction dir;
unsigned long size;
dir = V4L2_TYPE_IS_OUTPUT(vb->v4l2_buf.type) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE;
size = exact ?
vb2_get_plane_payload(vb, plane) : vb2_plane_size(vb, plane);
vb2_ion_sync_for_device((void *)vb2_plane_cookie(vb, plane), 0,
size, dir);
}
static void fimc_is_vb2_ion_plane_finish(struct fimc_is_vb2_buf *vbuf,
u32 plane, bool exact)
{
struct vb2_buffer *vb = &vbuf->vb;
enum dma_data_direction dir;
unsigned long size;
dir = V4L2_TYPE_IS_OUTPUT(vb->v4l2_buf.type) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE;
size = exact ?
vb2_get_plane_payload(vb, plane) : vb2_plane_size(vb, plane);
vb2_ion_sync_for_cpu((void *)vb2_plane_cookie(vb, plane), 0,
size, dir);
}
static void fimc_is_vb2_ion_buf_prepare(struct fimc_is_vb2_buf *vbuf, bool exact)
{
vb2_ion_buf_prepare(&vbuf->vb);
}
static void fimc_is_vb2_ion_buf_finish(struct fimc_is_vb2_buf *vbuf, bool exact)
{
vb2_ion_buf_finish(&vbuf->vb);
}
const struct fimc_is_vb2_buf_ops fimc_is_vb2_buf_ops_ion = {
.plane_kvaddr = fimc_is_vb2_ion_plane_kvaddr,
.plane_cookie = fimc_is_vb2_ion_plane_cookie,
.plane_dvaddr = fimc_is_vb2_ion_plane_dvaddr,
.plane_prepare = fimc_is_vb2_ion_plane_prepare,
.plane_finish = fimc_is_vb2_ion_plane_finish,
.buf_prepare = fimc_is_vb2_ion_buf_prepare,
.buf_finish = fimc_is_vb2_ion_buf_finish,
};
/* fimc-is private buffer operations */
static void fimc_is_vb2_ion_free(struct fimc_is_priv_buf *pbuf)
{
vb2_ion_private_free(pbuf->cookie);
kfree(pbuf);
}
static ulong fimc_is_vb2_ion_kvaddr(struct fimc_is_priv_buf *pbuf)
{
void *kva;
if (!pbuf)
return 0;
kva = vb2_ion_private_vaddr(pbuf->cookie);
if (IS_ERR_OR_NULL(kva)) {
err("could not get kernel addr for @%p: %ld",
pbuf->cookie, PTR_ERR(kva));
return 0;
}
return (ulong)kva;
}
static dma_addr_t fimc_is_vb2_ion_dvaddr(struct fimc_is_priv_buf *pbuf)
{
dma_addr_t dva = 0;
if (!pbuf)
return 0;
WARN_ON(vb2_ion_dma_address((void *)pbuf->cookie, &dva) != 0);
return (ulong)dva;
}
static phys_addr_t fimc_is_vb2_ion_phaddr(struct fimc_is_priv_buf *pbuf)
{
phys_addr_t pa = 0;
if (!pbuf)
return 0;
WARN_ON(vb2_ion_phys_address((void *)pbuf->cookie, &pa) != 0);
return pa;
}
static void fimc_is_vb2_ion_sync_for_device(struct fimc_is_priv_buf *pbuf,
off_t offset, size_t size, enum dma_data_direction dir)
{
vb2_ion_sync_for_device(pbuf->cookie, offset, size, dir);
}
static void fimc_is_vb2_ion_sync_for_cpu(struct fimc_is_priv_buf *pbuf,
off_t offset, size_t size, enum dma_data_direction dir)
{
vb2_ion_sync_for_cpu(pbuf->cookie, offset, size, dir);
}
const struct fimc_is_priv_buf_ops fimc_is_priv_buf_ops_ion = {
.free = fimc_is_vb2_ion_free,
.kvaddr = fimc_is_vb2_ion_kvaddr,
.dvaddr = fimc_is_vb2_ion_dvaddr,
.phaddr = fimc_is_vb2_ion_phaddr,
.sync_for_device = fimc_is_vb2_ion_sync_for_device,
.sync_for_cpu = fimc_is_vb2_ion_sync_for_cpu,
};
/* fimc-is memory operations */
static void *fimc_is_vb2_ion_init(struct platform_device *pdev,
long flag)
{
return vb2_ion_create_context(&pdev->dev, SZ_4K,
VB2ION_CTX_IOMMU |
flag);
}
static struct fimc_is_priv_buf *fimc_is_vb2_ion_alloc(void *ctx,
size_t size, size_t align)
{
struct fimc_is_priv_buf *buf;
int ret = 0;
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
buf->cookie = vb2_ion_private_alloc(ctx, size);
if (IS_ERR(buf->cookie)) {
err("failed to allocate a private buffer, size: %zd", size);
ret = PTR_ERR(buf->cookie);
goto err_priv_alloc;
}
buf->size = size;
buf->align = align;
buf->ctx = ctx;
buf->ops = &fimc_is_priv_buf_ops_ion;
return buf;
err_priv_alloc:
kfree(buf);
return ERR_PTR(ret);
}
static int fimc_is_vb2_ion_resume(void *ctx)
{
if (ctx)
return vb2_ion_attach_iommu(ctx);
return -ENOENT;
}
static void fimc_is_vb2_ion_suspend(void *ctx)
{
if (ctx)
vb2_ion_detach_iommu(ctx);
}
const struct fimc_is_mem_ops fimc_is_mem_ops_ion = {
.init = fimc_is_vb2_ion_init,
.cleanup = vb2_ion_destroy_context,
.resume = fimc_is_vb2_ion_resume,
.suspend = fimc_is_vb2_ion_suspend,
.set_cached = vb2_ion_set_cached,
.set_alignment = vb2_ion_set_alignment,
.alloc = fimc_is_vb2_ion_alloc,
};
#endif
/* fimc-is private buffer operations */
static void fimc_is_vb2_km_free(struct fimc_is_priv_buf *pbuf)
{
kfree(pbuf->kvaddr);
kfree(pbuf);
}
static ulong fimc_is_vb2_km_kvaddr(struct fimc_is_priv_buf *pbuf)
{
if (!pbuf)
return 0;
return (ulong)pbuf->kvaddr;
}
static phys_addr_t fimc_is_vb2_km_phaddr(struct fimc_is_priv_buf *pbuf)
{
phys_addr_t pa = 0;
if (!pbuf)
return 0;
pa = virt_to_phys(pbuf->kvaddr);
return pa;
}
const struct fimc_is_priv_buf_ops fimc_is_priv_buf_ops_km = {
.free = fimc_is_vb2_km_free,
.kvaddr = fimc_is_vb2_km_kvaddr,
.phaddr = fimc_is_vb2_km_phaddr,
};
static struct fimc_is_priv_buf *fimc_is_kmalloc(size_t size, size_t align)
{
struct fimc_is_priv_buf *buf = NULL;
int ret = 0;
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
buf->kvaddr = kzalloc(DEBUG_REGION_SIZE + 0x10, GFP_KERNEL);
if (!buf->kvaddr) {
ret = -ENOMEM;
goto err_priv_alloc;
}
buf->size = size;
buf->align = align;
buf->ops = &fimc_is_priv_buf_ops_km;
return buf;
err_priv_alloc:
kfree(buf);
return ERR_PTR(ret);
}
int fimc_is_mem_init(struct fimc_is_mem *mem, struct platform_device *pdev)
{
#if defined(CONFIG_VIDEOBUF2_ION)
mem->fimc_is_mem_ops = &fimc_is_mem_ops_ion;
mem->vb2_mem_ops = &vb2_ion_memops;
mem->fimc_is_vb2_buf_ops = &fimc_is_vb2_buf_ops_ion;
mem->kmalloc = &fimc_is_kmalloc;
#endif
mem->default_ctx = CALL_PTR_MEMOP(mem, init, pdev, VB2ION_CTX_VMCONTIG);
if (IS_ERR_OR_NULL(mem->default_ctx)) {
if (IS_ERR(mem->default_ctx))
return PTR_ERR(mem->default_ctx);
else
return -EINVAL;
}
mem->phcontig_ctx = CALL_PTR_MEMOP(mem, init, pdev, VB2ION_CTX_PHCONTIG);
if (IS_ERR_OR_NULL(mem->phcontig_ctx)) {
if (IS_ERR(mem->phcontig_ctx))
return PTR_ERR(mem->phcontig_ctx);
else
return -EINVAL;
}
return 0;
}

View file

@ -0,0 +1,166 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_MEM_H
#define FIMC_IS_MEM_H
#include <linux/platform_device.h>
#include <media/videobuf2-core.h>
#if defined(CONFIG_VIDEOBUF2_CMA_PHYS)
#include <media/videobuf2-cma-phys.h>
#elif defined(CONFIG_VIDEOBUF2_ION)
#include <media/videobuf2-ion.h>
#endif
struct fimc_is_vb2_buf;
struct fimc_is_vb2_buf_ops {
ulong (*plane_kvaddr)(struct fimc_is_vb2_buf *vbuf, u32 plane);
ulong (*plane_cookie)(struct fimc_is_vb2_buf *vbuf, u32 plane);
dma_addr_t (*plane_dvaddr)(struct fimc_is_vb2_buf *vbuf, u32 plane);
void (*plane_prepare)(struct fimc_is_vb2_buf *vbuf, u32 plane,
bool exact);
void (*plane_finish)(struct fimc_is_vb2_buf *vbuf, u32 plane,
bool exact);
void (*buf_prepare)(struct fimc_is_vb2_buf *vbuf, bool exact);
void (*buf_finish)(struct fimc_is_vb2_buf *vbuf, bool exact);
};
struct fimc_is_vb2_buf {
struct vb2_buffer vb;
ulong kva[VIDEO_MAX_PLANES];
dma_addr_t dva[VIDEO_MAX_PLANES];
const struct fimc_is_vb2_buf_ops *ops;
};
struct fimc_is_priv_buf;
struct fimc_is_priv_buf_ops {
void (*free)(struct fimc_is_priv_buf *pbuf);
ulong (*kvaddr)(struct fimc_is_priv_buf *pbuf);
dma_addr_t (*dvaddr)(struct fimc_is_priv_buf *pbuf);
phys_addr_t (*phaddr)(struct fimc_is_priv_buf *pbuf);
void (*sync_for_device)(struct fimc_is_priv_buf *pbuf,
off_t offset, size_t size,
enum dma_data_direction dir);
void (*sync_for_cpu)(struct fimc_is_priv_buf *pbuf,
off_t offset, size_t size,
enum dma_data_direction dir);
};
struct fimc_is_priv_buf {
void *cookie;
size_t size;
size_t align;
void *ctx;
void *kvaddr;
const struct fimc_is_priv_buf_ops *ops;
void *priv;
};
#define vb_to_fimc_is_vb2_buf(x) \
container_of(x, struct fimc_is_vb2_buf, vb)
#define CALL_BUFOP(buf, op, args...) \
((buf)->ops->op ? (buf)->ops->op(args) : 0)
#define CALL_PTR_BUFOP(buf, op, args...) \
((buf)->ops->op ? (buf)->ops->op(args) : NULL)
#define CALL_VOID_BUFOP(buf, op, args...) \
do { \
if ((buf)->ops->op) \
(buf)->ops->op(args); \
} while (0)
#define call_buf_op(buf, op, args...) \
((buf)->ops->op ? (buf)->ops->op((buf), args) : 0)
struct fimc_is_mem_ops {
void *(*init)(struct platform_device *pdev, long flag);
void (*cleanup)(void *ctx);
int (*resume)(void *ctx);
void (*suspend)(void *ctx);
void (*set_cached)(void *ctx, bool cacheable);
int (*set_alignment)(void *ctx, size_t alignment);
struct fimc_is_priv_buf *(*alloc)(void *ctx, size_t size, size_t align);
};
struct fimc_is_mem {
struct vb2_alloc_ctx *default_ctx;
struct vb2_alloc_ctx *phcontig_ctx;
const struct fimc_is_mem_ops *fimc_is_mem_ops;
const struct vb2_mem_ops *vb2_mem_ops;
const struct fimc_is_vb2_buf_ops *fimc_is_vb2_buf_ops;
void *priv;
struct fimc_is_priv_buf *(*kmalloc)(size_t size, size_t align);
};
#define CALL_MEMOP(mem, op, args...) \
((mem)->fimc_is_mem_ops->op ? \
(mem)->fimc_is_mem_ops->op(args) : 0)
#define CALL_PTR_MEMOP(mem, op, args...) \
((mem)->fimc_is_mem_ops->op ? \
(mem)->fimc_is_mem_ops->op(args) : NULL)
#define CALL_VOID_MEMOP(mem, op, args...) \
do { \
if ((mem)->fimc_is_mem_ops->op) \
(mem)->fimc_is_mem_ops->op(args); \
} while (0)
struct fimc_is_minfo {
struct fimc_is_priv_buf *pb_fw;
struct fimc_is_priv_buf *pb_setfile;
struct fimc_is_priv_buf *pb_rear_cal;
struct fimc_is_priv_buf *pb_front_cal;
struct fimc_is_priv_buf *pb_debug;
struct fimc_is_priv_buf *pb_fshared;
struct fimc_is_priv_buf *pb_dregion;
struct fimc_is_priv_buf *pb_pregion;
struct fimc_is_priv_buf *pb_lib;
struct fimc_is_priv_buf *pb_taaisp;
struct fimc_is_priv_buf *pb_lhfd;
struct fimc_is_priv_buf *pb_vra;
struct fimc_is_priv_buf *pb_tpu;
size_t total_size;
ulong kvaddr_debug_cnt;
dma_addr_t dvaddr;
ulong kvaddr;
dma_addr_t dvaddr_lib;
ulong kvaddr_lib;
phys_addr_t phaddr_debug;
dma_addr_t dvaddr_debug;
ulong kvaddr_debug;
dma_addr_t dvaddr_fshared;
ulong kvaddr_fshared;
dma_addr_t dvaddr_region;
ulong kvaddr_region;
dma_addr_t dvaddr_taaisp;
ulong kvaddr_taaisp;
dma_addr_t dvaddr_tpu;
ulong kvaddr_tpu;
dma_addr_t dvaddr_lhfd; /* FD map buffer region */
ulong kvaddr_lhfd; /* NUM_FD_INTERNAL_BUF = 3 */
dma_addr_t dvaddr_vra;
ulong kvaddr_vra;
ulong kvaddr_setfile;
ulong kvaddr_rear_cal;
ulong kvaddr_front_cal;
};
int fimc_is_mem_init(struct fimc_is_mem *mem, struct platform_device *pdev);
#endif

View file

@ -0,0 +1,649 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is pipe functions
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/bug.h>
#include "fimc-is-pipe.h"
#include "fimc-is-core.h"
#include "fimc-is-video.h"
#include "fimc-is-framemgr.h"
int fimc_is_pipe_probe(struct fimc_is_pipe *pipe)
{
int ret = 0;
int i = 0;
pipe->src = NULL;
pipe->dst = NULL;
for (i = 0; i < PIPE_SLOT_MAX; i++)
pipe->vctx[i] = NULL;
return ret;
}
/*
* This function must be called in case of FIMC_IS_GROUP_PIPE_INPUT.
*/
static int fimc_is_cancel_all_cap_nodes(struct fimc_is_device_ischain *device,
struct fimc_is_pipe *pipe,
struct fimc_is_video_ctx *src_vctx,
struct fimc_is_video_ctx *junction_vctx,
struct fimc_is_framemgr *src_framemgr,
struct fimc_is_frame *src_frame)
{
int ret = 0;
int capture_id;
unsigned long flags;
struct camera2_node *cap_node;
struct fimc_is_subdev *junction;
struct fimc_is_subdev *subdev;
struct fimc_is_video_ctx *sub_vctx;
struct fimc_is_framemgr *sub_framemgr;
struct fimc_is_frame *sub_frame;
junction = (struct fimc_is_subdev *)junction_vctx->subdev;
for (capture_id = 0; capture_id < CAPTURE_NODE_MAX; ++capture_id) {
cap_node = &src_frame->shot_ext->node_group.capture[capture_id];
if (!cap_node || !cap_node->request ||
cap_node->vid == junction->vid)
continue;
subdev = video2subdev(FIMC_IS_ISCHAIN_SUBDEV, (void *)device, cap_node->vid);
if (!subdev) {
mperr("video2subdev is fail", device, pipe, src_vctx->video);
continue;
}
sub_vctx = subdev->vctx;
if (!sub_vctx) {
mperr("sub_vctx is null", device, pipe, src_vctx->video);
ret = -EINVAL;
continue;
}
sub_framemgr = GET_SUBDEV_FRAMEMGR(subdev);
framemgr_e_barrier_irqs(sub_framemgr, FMGR_IDX_28, flags);
/* cancel "request" buffer not "process" buffer */
sub_frame = peek_frame(sub_framemgr, FS_REQUEST);
if (sub_frame) {
sub_frame->stream->fcount = src_frame->fcount;
sub_frame->stream->fvalid = 0;
trans_frame(sub_framemgr, sub_frame, FS_COMPLETE);
CALL_VOPS(sub_vctx, done, sub_frame->index, VB2_BUF_STATE_ERROR);
mpwarn("[F%d] CANCEL(%d)", device, pipe, sub_vctx->video,
sub_frame->fcount, sub_frame->index);
} else {
mperr("subframe(%d) is NULL..", device, pipe, src_vctx->video, cap_node->vid);
}
framemgr_x_barrier_irqr(sub_framemgr, FMGR_IDX_28, flags);
}
return ret;
}
/*
* This function perform the same dqbuf.
* But it gurantees that the v4l2_buffer's value shouldn't be touched.
* Also this function acquire the video_device mutex lock instead of v4l2 framework.
* And it return the buf index.
*/
static int fimc_is_pipe_dqbuf_proxy(struct fimc_is_video_ctx *vctx,
struct v4l2_buffer *buf,
bool blocking)
{
struct v4l2_buffer v4l2_buf;
struct v4l2_plane planes[VIDEO_MAX_PLANES];
struct fimc_is_video *video;
int ret;
#ifdef DBG_STREAMING
mvinfo("%s\n", vctx, vctx->video, __func__);
#endif
memset(&v4l2_buf, 0, sizeof(struct v4l2_buffer));
memset(planes, 0, sizeof(struct v4l2_plane) * VIDEO_MAX_PLANES);
v4l2_buf.type = buf->type;
v4l2_buf.memory = buf->memory;
v4l2_buf.length = buf->length;
v4l2_buf.m.planes = planes;
video = vctx->video;
ret = mutex_lock_interruptible(&video->lock);
if (ret) {
mverr("mutex_lock_interruptible is fail(%d)", vctx, video, ret);
return ret;
}
ret = fimc_is_video_dqbuf(vctx, &v4l2_buf, blocking);
if (ret) {
err("fimc_is_video_dqbuf is fail(%d)", ret);
mutex_unlock(&video->lock);
return ret;
}
mutex_unlock(&video->lock);
#ifdef DBG_STREAMING
mvinfo("%s done(%d)\n", vctx, vctx->video, __func__, v4l2_buf.index);
#endif
return v4l2_buf.index;
}
/*
* This function perform the same dqbuf.
* this function acquire the video_device mutex lock instead of v4l2 framework.
*/
static int fimc_is_pipe_qbuf_proxy(struct fimc_is_video_ctx *vctx,
struct v4l2_buffer *buf)
{
int ret;
struct fimc_is_video *video;
#ifdef DBG_STREAMING
mvinfo("%s(%d)\n", vctx, vctx->video, __func__, buf->index);
#endif
video = vctx->video;
ret = mutex_lock_interruptible(&video->lock);
if (ret) {
mverr("mutex_lock_interruptible is fail(%d)", vctx, video, ret);
return ret;
}
ret = fimc_is_video_qbuf(vctx, buf);
if (ret)
err("fimc_is_video_qbuf is fail(%d)", ret);
mutex_unlock(&video->lock);
return ret;
}
static int fimc_is_pipe_callback(struct fimc_is_device_ischain *device,
struct fimc_is_group *group,
struct fimc_is_frame *frame)
{
int ret = 0;
u32 timeout = 3000;
unsigned long flags;
u32 capture_id, index;
struct fimc_is_pipe *pipe;
struct fimc_is_framemgr *src_framemgr, *junction_framemgr, *dst_framemgr;
struct fimc_is_frame *junction_frame = NULL;
struct fimc_is_frame *dst_frame = NULL;
struct fimc_is_video_ctx *src_vctx, *junction_vctx, *dst_vctx;
struct fimc_is_subdev *junction;
struct camera2_node *node;
struct fimc_is_queue *queue;
BUG_ON(!device);
BUG_ON(!group);
BUG_ON(!group->leader.vctx);
if (!test_bit(FIMC_IS_GROUP_PIPE_OUTPUT, &group->state))
goto p_err;
#ifdef DBG_STREAMING
mpinfo("%s(%d)\n", group->leader.vctx, group->leader.vctx->video, __func__, frame->index);
#endif
pipe = &device->pipe;
/*
* junction pipe qbuf before src pipe shot
* In case of PIPE_INPUT, HAL does not call q operation for junction.
* HAL must call prepare_buf for source video node!!
*/
junction_vctx = pipe->vctx[PIPE_SLOT_JUNCTION];
junction_framemgr = GET_FRAMEMGR(junction_vctx);
if (!junction_framemgr) {
merr("junction_framemgr is NULL", device);
ret = -EINVAL;
goto p_err;
}
junction = (struct fimc_is_subdev *)junction_vctx->subdev;
src_vctx = pipe->vctx[PIPE_SLOT_SRC];
src_framemgr = GET_FRAMEMGR(src_vctx);
if (!src_framemgr) {
merr("src_framemgr is NULL", device);
ret = -EINVAL;
goto p_err;
}
dst_vctx = pipe->vctx[PIPE_SLOT_DST];
dst_framemgr = GET_FRAMEMGR(dst_vctx);
if (!dst_framemgr) {
merr("dst_framemgr is NULL", device);
ret = -EINVAL;
goto p_err;
}
for (capture_id = 0; capture_id < CAPTURE_NODE_MAX; ++capture_id) {
node = &frame->shot_ext->node_group.capture[capture_id];
if (junction->vid == node->vid) {
/* eanble request for junction ip of pipe source group */
node->request = 1;
break;
}
}
if (capture_id == CAPTURE_NODE_MAX) {
mperr("can't find vid(%d)", device, pipe, junction_vctx->video, junction->vid);
ret = -EINVAL;
goto p_err;
}
/* get a free junction frame */
framemgr_e_barrier_irqs(junction_framemgr, pipe->id, flags);
junction_frame = peek_frame(junction_framemgr, FS_FREE);
framemgr_x_barrier_irqr(junction_framemgr, pipe->id, flags);
if (!junction_frame) {
mpwarn("[F%d] no free junction frame", device, pipe, junction_vctx->video, frame->fcount);
ret = -EINVAL;
goto p_err;
}
index = junction_frame->index;
dst_frame = &dst_framemgr->frames[index];
/* wait for available same buffer using by dst */
do {
framemgr_e_barrier_irqs(dst_framemgr, pipe->id, flags);
if (dst_frame->state == FS_REQUEST ||
dst_frame->state == FS_PROCESS)
junction_frame = NULL;
else
junction_frame = &junction_framemgr->frames[index];
framemgr_x_barrier_irqr(dst_framemgr, pipe->id, flags);
if (!junction_frame) {
mdelay(1);
timeout--;
}
} while(!junction_frame && timeout);
if (!timeout) {
mpwarn("[F%d] junction frame(%d) is being used in DST..",
device, pipe, junction_vctx->video,
dst_frame->fcount, index);
/* cancel all capture node buffer */
fimc_is_cancel_all_cap_nodes(device, pipe,
src_vctx, junction_vctx, src_framemgr, frame);
ret = -EINVAL;
goto p_err;
}
/*
* Qbuf Destination
* We modified the videobuf's state to PREPARED from DEQUEUED
* to skip the unnecessary process(__buf_prepare) in this case.
* Also, without this setting, the problem that fimc_is_frame's device addr
* and vb2_queue's device addr are different can be happened.
*/
queue = GET_QUEUE(junction_vctx);
if (queue && queue->vbq && queue->vbq->bufs[index])
queue->vbq->bufs[index]->state = VB2_BUF_STATE_PREPARED;
ret = CALL_VOPS(junction_vctx, qbuf, &pipe->buf[PIPE_SLOT_JUNCTION][index]);
if (ret) {
mperr("[F%d] fimc_is_video_qbuf is fail(%d)",
device, pipe, junction_vctx->video, frame->fcount, ret);
ret = -EINVAL;
goto p_err;
}
p_err:
return ret;
};
static int fimc_is_pipe_qbuf(struct fimc_is_video_ctx *vctx,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_device_ischain *device;
struct fimc_is_pipe *pipe;
struct fimc_is_video_ctx *src_vctx, *junction_vctx, *dst_vctx;
bool qbuf_proxy = false;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
#ifdef DBG_STREAMING
mpinfo("%s(%d)\n", vctx, vctx->video, __func__, buf->index);
#endif
device = GET_DEVICE(vctx);
pipe = &device->pipe;
src_vctx = pipe->vctx[PIPE_SLOT_SRC];
if (!src_vctx || !src_vctx->video) {
mperr("src_vctx or video is null", device, pipe, vctx->video);
ret = -EINVAL;
goto p_err;
}
junction_vctx = pipe->vctx[PIPE_SLOT_JUNCTION];
if (!junction_vctx || !junction_vctx->video) {
mperr("junction_vctx or video is null", device, pipe, vctx->video);
ret = -EINVAL;
goto p_err;
}
dst_vctx = pipe->vctx[PIPE_SLOT_DST];
if (!dst_vctx || !dst_vctx->video) {
mperr("dst_vctx or video is null", device, pipe, vctx->video);
ret = -EINVAL;
goto p_err;
}
if (test_bit(FIMC_IS_GROUP_PIPE_INPUT, &pipe->dst->state)) {
if (junction_vctx->video->id == vctx->video->id ||
dst_vctx->video->id == vctx->video->id) {
qbuf_proxy = true;
}
} else {
if (dst_vctx->video->id == vctx->video->id)
qbuf_proxy = true;
}
if (qbuf_proxy)
ret = fimc_is_pipe_qbuf_proxy(vctx, buf);
else
ret = fimc_is_video_qbuf(vctx, buf);
if (ret)
mperr("fimc_is_video_qbuf(%d) is fail(%d)", device, pipe, vctx->video, buf->index, ret);
p_err:
return ret;
}
static int fimc_is_pipe_dqbuf(struct fimc_is_video_ctx *vctx,
struct v4l2_buffer *buf,
bool blocking)
{
int ret = 0;
unsigned long flags;
int index, src_findex, capture_id;
struct fimc_is_device_ischain *device;
struct fimc_is_pipe *pipe;
struct fimc_is_framemgr *src_framemgr, *junction_framemgr, *dst_framemgr;
struct fimc_is_frame *src_frame, *junction_frame, *dst_frame;
struct fimc_is_video_ctx *src_vctx, *junction_vctx, *dst_vctx;
struct camera2_node *dst_node, *cap_node;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
#ifdef DBG_STREAMING
mpinfo("%s\n", vctx, vctx->video, __func__);
#endif
device = GET_DEVICE(vctx);
pipe = &device->pipe;
src_vctx = pipe->vctx[PIPE_SLOT_SRC];
if (!src_vctx || !src_vctx->video) {
mperr("src_vctx or video is null", device, pipe, vctx->video);
ret = -EINVAL;
goto pipe_pass;
}
junction_vctx = pipe->vctx[PIPE_SLOT_JUNCTION];
if (!junction_vctx || !junction_vctx->video) {
mperr("junction_vctx or video is null", device, pipe, vctx->video);
ret = -EINVAL;
goto pipe_pass;
}
dst_vctx = pipe->vctx[PIPE_SLOT_DST];
if (!dst_vctx || !dst_vctx->video) {
mperr("dst_vctx or video is null", device, pipe, vctx->video);
ret = -EINVAL;
goto pipe_pass;
}
if (src_vctx->video->id == vctx->video->id) {
/* Source */
ret = fimc_is_video_dqbuf(src_vctx, buf, blocking);
src_framemgr = GET_FRAMEMGR(src_vctx);
dst_framemgr = GET_FRAMEMGR(dst_vctx);
src_frame = &src_framemgr->frames[buf->index];
if (ret) {
mperr("fimc_is_video_dqbuf is fail(%d)", device, pipe, vctx->video, ret);
} else if (!(buf->flags & V4L2_BUF_FLAG_ERROR)) {
/*
* In case of giving a feedback(dynamic meta) from dst to src,
* the integrity has to be guaranteed with a spinlock.
* For this process, dst's framemgr spinlock can be used.
*/
framemgr_e_barrier_irqs(dst_framemgr, pipe->id, flags);
memcpy(&src_frame->shot->dm.stats, &pipe->pipe_dm.stats, sizeof(struct camera2_stats_dm));
framemgr_x_barrier_irqr(dst_framemgr, pipe->id, flags);
}
/*
* junction dqbuf after buffer done of junction
* In case of PIPE_INPUT, HAL does not call dq operation for junction
*/
if (test_bit(FIMC_IS_GROUP_PIPE_INPUT, &pipe->dst->state)) {
int tmp_ret = 0;
/* error handling, set all capture node to error buffer done */
if (buf->flags & V4L2_BUF_FLAG_ERROR) {
mperr("cancel capture nodes(%d)",
device, pipe, vctx->video, buf->index);
/* cancel all capture node buffer */
tmp_ret = fimc_is_cancel_all_cap_nodes(device, pipe,
src_vctx, junction_vctx, src_framemgr, src_frame);
if (tmp_ret) {
mperr("can't cancel capture nodes(%d/%d)",
device, pipe, vctx->video, buf->index, tmp_ret);
goto pipe_pass;
}
}
tmp_ret = CALL_VOPS(junction_vctx, dqbuf, &pipe->buf[PIPE_SLOT_JUNCTION][0], blocking);
if (tmp_ret) {
mperr("fimc_is_video_dqbuf is fail(%d)", device, pipe, vctx->video, tmp_ret);
goto pipe_pass;
}
}
} else if (junction_vctx->video->id == vctx->video->id) {
/* Junction */
if (test_bit(FIMC_IS_GROUP_PIPE_INPUT, &pipe->dst->state)) {
index = fimc_is_pipe_dqbuf_proxy(junction_vctx, buf, blocking);
if (index < 0) {
ret = index;
mperr("fimc_is_video_dqbuf is fail(%d)", device, pipe, vctx->video, ret);
goto pipe_pass;
}
} else {
ret = fimc_is_video_dqbuf(junction_vctx, buf, blocking);
if (ret) {
mperr("fimc_is_video_dqbuf is fail(%d)", device, pipe, vctx->video, ret);
goto pipe_pass;
}
index = buf->index;
}
junction_framemgr = GET_FRAMEMGR(vctx);
src_framemgr = GET_FRAMEMGR(src_vctx);
dst_framemgr = GET_FRAMEMGR(dst_vctx);
junction_frame = &junction_framemgr->frames[index];
src_findex = junction_frame->stream->findex;
src_frame = &src_framemgr->frames[src_findex];
dst_frame = &dst_framemgr->frames[index];
dst_node = &dst_frame->shot_ext->node_group.leader;
/* frmae shot done check and pipe ip dqbuf */
if (dst_frame->state == FS_COMPLETE) {
ret = CALL_VOPS(dst_vctx, dqbuf, &pipe->buf[PIPE_SLOT_DST][0], blocking);
if (ret < 0) {
mperr("fimc_is_video_dqbuf is fail(%d)", device, pipe, vctx->video, ret);
goto pipe_pass;
}
}
/* pipe source group junction ip NDONE check */
if (!junction_frame->stream->fvalid) {
mperr("pipe source junction NDONE(%d, %d/%d)", device, pipe, vctx->video,
src_findex, junction_frame->index, junction_frame->stream->fvalid);
goto pipe_pass;
}
/* pipe ip shot meta copy */
memcpy(dst_frame->shot_ext, src_frame->shot_ext, sizeof(struct camera2_shot_ext));
/* pipe ip crop region update */
for (capture_id = 0; capture_id < CAPTURE_NODE_MAX; ++capture_id) {
cap_node = &src_frame->shot_ext->node_group.capture[capture_id];
if (cap_node->vid == vctx->video->id) {
dst_node->input.cropRegion[0] = cap_node->output.cropRegion[0];
dst_node->input.cropRegion[1] = cap_node->output.cropRegion[1];
dst_node->input.cropRegion[2] = cap_node->output.cropRegion[2];
dst_node->input.cropRegion[3] = cap_node->output.cropRegion[3];
break;
}
}
if (capture_id == CAPTURE_NODE_MAX) {
dst_node->input.cropRegion[0] = 0;
dst_node->input.cropRegion[1] = 0;
dst_node->input.cropRegion[2] = 0;
dst_node->input.cropRegion[3] = 0;
mperr("pipe junction capture id is invalid", device, pipe, vctx->video);
}
/*
* Qbuf Destination
* We modified the videobuf's state to PREPARED from DEQUEUED
* to skip the unnecessary process(__buf_prepare) in this case.
* Also, without this setting, the problem that fimc_is_frame's device addr
* and vb2_queue's device addr are different can be happened.
*/
queue = GET_QUEUE(dst_vctx);
if (queue && queue->vbq && queue->vbq->bufs[index])
queue->vbq->bufs[index]->state = VB2_BUF_STATE_PREPARED;
ret = CALL_VOPS(dst_vctx, qbuf, &pipe->buf[PIPE_SLOT_DST][index]);
if (ret)
mperr("[F%d] fimc_is_video_qbuf is fail(%d)", device, pipe, vctx->video, index, ret);
} else if (dst_vctx->video->id == vctx->video->id) {
/* Destination */
ret = fimc_is_pipe_dqbuf_proxy(dst_vctx, buf, blocking);
if (ret < 0) {
mperr("fimc_is_video_dqbuf is fail(%d)", device, pipe, vctx->video, ret);
goto pipe_pass;
}
} else {
mperr("There's no matched vctx for dqbuf", device, pipe, vctx->video);
ret = -EINVAL;
goto pipe_pass;
}
pipe_pass:
return ret;
}
static int fimc_is_pipe_done(struct fimc_is_video_ctx *vctx,
u32 index, u32 state)
{
int ret = 0;
struct fimc_is_device_ischain *device;
struct fimc_is_pipe *pipe;
struct fimc_is_framemgr *dst_framemgr;
struct fimc_is_frame *dst_frame;
struct fimc_is_video_ctx *dst_vctx;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
#ifdef DBG_STREAMING
mpinfo("%s(%d)\n", vctx, vctx->video, __func__, index);
#endif
device = GET_DEVICE(vctx);
pipe = &device->pipe;
dst_vctx = pipe->vctx[PIPE_SLOT_DST];
if (!dst_vctx || !dst_vctx->video) {
mperr("dst_vctx or video is null", device, pipe, vctx->video);
ret = -EINVAL;
goto p_err;
}
if (dst_vctx->video->id == vctx->video->id) {
dst_framemgr = GET_FRAMEMGR(dst_vctx);
dst_frame = &dst_framemgr->frames[index];
/* dm meta copy */
memcpy(&pipe->pipe_dm, &dst_frame->shot->dm, sizeof(struct camera2_dm));
}
ret = fimc_is_video_buffer_done(vctx, index, state);
if (ret)
mperr("fimc_is_video_buffer_done is fail(%d)", device, pipe, vctx->video, ret);
p_err:
return ret;
}
int fimc_is_pipe_create(struct fimc_is_pipe *pipe,
struct fimc_is_group *src,
struct fimc_is_group *dst)
{
int ret = 0;
int i = 0;
BUG_ON(!pipe);
BUG_ON(!src);
BUG_ON(!src->leader.vctx);
BUG_ON(!src->junction->vctx);
BUG_ON(!dst);
BUG_ON(!dst->leader.vctx);
pipe->id = 0;
pipe->src = src;
pipe->dst = dst;
src->pipe_shot_callback = fimc_is_pipe_callback;
dst->pipe_shot_callback = fimc_is_pipe_callback;
pipe->vctx[PIPE_SLOT_SRC] = src->leader.vctx;
pipe->vctx[PIPE_SLOT_JUNCTION] = src->tail->junction->vctx;
pipe->vctx[PIPE_SLOT_DST] = dst->leader.vctx;
for (i = 0; i < PIPE_SLOT_MAX; i++) {
pipe->vctx[i]->vops.qbuf = fimc_is_pipe_qbuf;
pipe->vctx[i]->vops.dqbuf = fimc_is_pipe_dqbuf;
pipe->vctx[i]->vops.done = fimc_is_pipe_done;
memset(pipe->buf[i], 0x0, (sizeof(struct v4l2_buffer) * FIMC_IS_MAX_BUFS));
memset(pipe->planes[i], 0x0, (sizeof(struct v4l2_plane) * FIMC_IS_MAX_BUFS * VIDEO_MAX_PLANES));
}
return ret;
}

View file

@ -0,0 +1,38 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_PIPE_H
#define FIMC_IS_PIPE_H
#include "fimc-is-groupmgr.h"
enum pipe_slot_type {
PIPE_SLOT_SRC,
PIPE_SLOT_JUNCTION,
PIPE_SLOT_DST,
PIPE_SLOT_MAX,
};
struct fimc_is_pipe {
u32 id;
struct fimc_is_group *src;
struct fimc_is_group *dst;
struct fimc_is_video_ctx *vctx[PIPE_SLOT_MAX];
struct camera2_dm pipe_dm;
struct camera2_udm pipe_udm;
struct v4l2_buffer buf[PIPE_SLOT_MAX][FIMC_IS_MAX_BUFS];
struct v4l2_plane planes[PIPE_SLOT_MAX][FIMC_IS_MAX_BUFS][FIMC_IS_MAX_PLANES];
};
int fimc_is_pipe_probe(struct fimc_is_pipe *pipe);
int fimc_is_pipe_create(struct fimc_is_pipe *pipe,
struct fimc_is_group *src,
struct fimc_is_group *dst);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,152 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_RESOURCE_MGR_H
#define FIMC_IS_RESOURCE_MGR_H
#include <linux/notifier.h>
#include "fimc-is-groupmgr.h"
#include "fimc-is-interface.h"
#define RESOURCE_TYPE_SENSOR0 0
#define RESOURCE_TYPE_SENSOR1 1
#define RESOURCE_TYPE_SENSOR2 2
#define RESOURCE_TYPE_SENSOR3 3
#define RESOURCE_TYPE_SENSOR4 4
#define RESOURCE_TYPE_SENSOR5 5
#define RESOURCE_TYPE_ISCHAIN 6
#define RESOURCE_TYPE_PREPROC 7
#define RESOURCE_TYPE_MAX 8
enum fimc_is_resourcemgr_state {
FIMC_IS_RM_COM_POWER_ON,
FIMC_IS_RM_SS0_POWER_ON,
FIMC_IS_RM_SS1_POWER_ON,
FIMC_IS_RM_SS2_POWER_ON,
FIMC_IS_RM_SS3_POWER_ON,
FIMC_IS_RM_SS4_POWER_ON,
FIMC_IS_RM_SS5_POWER_ON,
FIMC_IS_RM_ISC_POWER_ON,
FIMC_IS_RM_POWER_ON
};
enum fimc_is_dvfs_state {
FIMC_IS_DVFS_SEL_TABLE
};
struct fimc_is_dvfs_ctrl {
struct mutex lock;
int cur_int_qos;
int cur_mif_qos;
int cur_cam_qos;
int cur_i2c_qos;
int cur_disp_qos;
int cur_hpg_qos;
int cur_hmp_bst;
u32 dvfs_table_idx;
u32 dvfs_table_max;
ulong state;
struct fimc_is_dvfs_scenario_ctrl *static_ctrl;
struct fimc_is_dvfs_scenario_ctrl *dynamic_ctrl;
struct fimc_is_dvfs_scenario_ctrl *external_ctrl;
};
struct fimc_is_clk_gate_ctrl {
spinlock_t lock;
unsigned long msk_state;
int msk_cnt[GROUP_ID_MAX];
u32 msk_lock_by_ischain[FIMC_IS_STREAM_COUNT];
struct exynos_fimc_is_clk_gate_info *gate_info;
u32 msk_clk_on_off_state; /* on/off(1/0) state per ip */
/*
* For check that there's too long clock-on period.
* This var will increase when clock on,
* And will decrease when clock off.
*/
unsigned long chk_on_off_cnt[GROUP_ID_MAX];
};
struct fimc_is_static_mem {
ulong paddr;
ulong vaddr;
ulong size;
};
struct fimc_is_resource {
struct platform_device *pdev;
void __iomem *regs;
atomic_t rsccount;
u32 private_data;
};
struct fimc_is_resourcemgr {
unsigned long state;
atomic_t rsccount;
atomic_t rsccount_module; /* sensor module */
struct fimc_is_resource resource_preproc;
struct fimc_is_resource resource_sensor0;
struct fimc_is_resource resource_sensor1;
struct fimc_is_resource resource_sensor2;
struct fimc_is_resource resource_sensor3;
struct fimc_is_resource resource_sensor4;
struct fimc_is_resource resource_sensor5;
struct fimc_is_resource resource_ischain;
struct fimc_is_mem mem;
struct fimc_is_minfo minfo;
struct fimc_is_dvfs_ctrl dvfs_ctrl;
struct fimc_is_clk_gate_ctrl clk_gate_ctrl;
u32 cluster0;
u32 cluster1;
u32 hal_version;
#ifdef ENABLE_FW_SHARE_DUMP
ulong fw_share_dump_buf;
#endif
/* tmu */
struct notifier_block tmu_notifier;
u32 tmu_state;
u32 limited_fps;
/* bus monitor */
struct notifier_block bm_notifier;
void *private_data;
#ifdef ENABLE_SHARED_METADATA
/* shared meta data */
spinlock_t shared_meta_lock;
struct camera2_shot shared_shot;
#endif
};
int fimc_is_resourcemgr_probe(struct fimc_is_resourcemgr *resourcemgr, void *private_data);
int fimc_is_resource_open(struct fimc_is_resourcemgr *resourcemgr, u32 rsc_type, void **device);
int fimc_is_resource_get(struct fimc_is_resourcemgr *resourcemgr, u32 rsc_type);
int fimc_is_resource_put(struct fimc_is_resourcemgr *resourcemgr, u32 rsc_type);
int fimc_is_resource_ioctl(struct fimc_is_resourcemgr *resourcemgr, struct v4l2_control *ctrl);
int fimc_is_logsync(struct fimc_is_interface *itf, u32 sync_id, u32 msg_test_id);
int fimc_is_resource_dump(void);
#define GET_RESOURCE(resourcemgr, type) \
((type == RESOURCE_TYPE_SENSOR0) ? &resourcemgr->resource_sensor0 : \
((type == RESOURCE_TYPE_SENSOR1) ? &resourcemgr->resource_sensor1 : \
((type == RESOURCE_TYPE_SENSOR2) ? &resourcemgr->resource_sensor2 : \
((type == RESOURCE_TYPE_SENSOR3) ? &resourcemgr->resource_sensor3 : \
((type == RESOURCE_TYPE_SENSOR4) ? &resourcemgr->resource_sensor4 : \
((type == RESOURCE_TYPE_SENSOR5) ? &resourcemgr->resource_sensor5 : \
((type == RESOURCE_TYPE_ISCHAIN) ? &resourcemgr->resource_ischain : \
((type == RESOURCE_TYPE_PREPROC) ? &resourcemgr->resource_preproc : \
NULL))))))))
#endif

View file

@ -0,0 +1,251 @@
/*
* driver for FIMC-IS SPI
*
* Copyright (c) 2011, Samsung Electronics. All rights reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include "fimc-is-config.h"
#include "fimc-is-core.h"
#include "fimc-is-dt.h"
#include "fimc-is-regs.h"
#define STREAM_TO_U16(var16, p) {(var16) = ((u16)(*((u8 *)p+1)) + \
((u8)(*((u8 *)p) << 8))); }
int fimc_is_spi_s_pin(struct fimc_is_spi *spi, int state)
{
struct pinctrl_state *ssn_pin;
struct pinctrl_state *parent_pin;
int ret = 0, ret2 = 0;
if (!spi->use_spi_pinctrl)
return 0;
switch (state) {
case SPI_PIN_STATE_HOST:
parent_pin = spi->parent_pin_fn;
ssn_pin = spi->pin_ssn_out;
break;
case SPI_PIN_STATE_ISP_FW:
parent_pin = spi->parent_pin_fn;
ssn_pin = spi->pin_ssn_fn;
break;
case SPI_PIN_STATE_IDLE:
default:
parent_pin = spi->parent_pin_out;
ssn_pin = spi->pin_ssn_out;
break;
}
ret = pinctrl_select_state(spi->parent_pinctrl, parent_pin);
if (ret < 0) {
pr_err("pinctrl_select_state is fail(%d)\n", ret);
}
ret2 = pinctrl_select_state(spi->pinctrl, ssn_pin);
if (ret2 < 0) {
pr_err("pinctrl_select_state is fail(%d)\n", ret2);
ret |= ret2;
}
return ret;
}
#ifdef CONFIG_SPI
int fimc_is_spi_reset(struct fimc_is_spi *spi)
{
int ret = 0;
unsigned char req_rst[1] = { 0x99 };
struct spi_transfer t_c;
struct spi_transfer t_r;
struct spi_message m;
BUG_ON(!spi->device);
memset(&t_c, 0x00, sizeof(t_c));
memset(&t_r, 0x00, sizeof(t_r));
t_c.tx_buf = req_rst;
t_c.len = 1;
t_c.cs_change = 0;
spi_message_init(&m);
spi_message_add_tail(&t_c, &m);
ret = spi_sync(spi->device, &m);
if (ret) {
err("spi sync error - can't get device information");
ret = -EIO;
goto p_err;
}
p_err:
return ret;
}
int fimc_is_spi_read(struct fimc_is_spi *spi, void *buf, u32 addr, size_t size)
{
int ret = 0;
unsigned char req_data[4];
struct spi_transfer t_c;
struct spi_transfer t_r;
struct spi_message m;
BUG_ON(!spi->device);
memset(&t_c, 0x00, sizeof(t_c));
memset(&t_r, 0x00, sizeof(t_r));
req_data[0] = 0x3;
req_data[1] = (addr & 0xFF0000) >> 16;
req_data[2] = (addr & 0xFF00) >> 8;
req_data[3] = (addr & 0xFF);
t_c.tx_buf = req_data;
t_c.len = 4;
t_c.cs_change = 1;
t_c.bits_per_word = 32;
t_r.rx_buf = buf;
t_r.len = (u32)size;
t_r.cs_change = 0;
switch (size % 4) {
case 0:
t_r.bits_per_word = 32;
break;
case 2:
t_r.bits_per_word = 16;
break;
default:
t_r.bits_per_word = 8;
break;
}
spi_message_init(&m);
spi_message_add_tail(&t_c, &m);
spi_message_add_tail(&t_r, &m);
ret = spi_sync(spi->device, &m);
if (ret) {
err("spi sync error - can't read data");
ret = -EIO;
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_spi_probe(struct spi_device *device)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_spi *spi;
dbg_core("%s\n", __func__);
if (fimc_is_dev == NULL) {
probe_warn("fimc_is_dev is not yet probed(spi)");
return -EPROBE_DEFER;
}
core = (struct fimc_is_core *)dev_get_drvdata(fimc_is_dev);
if (!core) {
probe_err("core is NULL");
ret = -EINVAL;
goto p_err;
}
/* spi->bits_per_word = 16; */
if (spi_setup(device)) {
probe_err("failed to setup spi for fimc_is_spi\n");
ret = -EINVAL;
goto p_err;
}
if (!strncmp(device->modalias, "fimc_is_spi0", 12)) {
spi = &core->spi0;
spi->node = "samsung,fimc_is_spi0";
spi->device = device;
ret = fimc_is_spi_parse_dt(spi);
if (ret) {
probe_err("[%s] of_fimc_is_spi_dt parse dt failed\n", __func__);
return ret;
}
}
if (!strncmp(device->modalias, "fimc_is_spi1", 12)) {
spi = &core->spi1;
spi->node = "samsung,fimc_is_spi1";
spi->device = device;
ret = fimc_is_spi_parse_dt(spi);
if (ret) {
probe_err("[%s] of_fimc_is_spi_dt parse dt failed\n", __func__);
return ret;
}
}
p_err:
probe_info("[SPI] %s(%s):%d\n", __func__, device->modalias, ret);
return ret;
}
static int fimc_is_spi_remove(struct spi_device *spi)
{
return 0;
}
static const struct of_device_id exynos_fimc_is_spi_match[] = {
{
.compatible = "samsung,fimc_is_spi0",
},
{
.compatible = "samsung,fimc_is_spi1",
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_fimc_is_spi_match);
static struct spi_driver fimc_is_spi0_driver = {
.driver = {
.name = "fimc_is_spi0",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = exynos_fimc_is_spi_match,
},
.probe = fimc_is_spi_probe,
.remove = fimc_is_spi_remove,
};
module_spi_driver(fimc_is_spi0_driver);
static struct spi_driver fimc_is_spi1_driver = {
.driver = {
.name = "fimc_is_spi1",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = exynos_fimc_is_spi_match,
},
.probe = fimc_is_spi_probe,
.remove = fimc_is_spi_remove,
};
module_spi_driver(fimc_is_spi1_driver);
#endif
MODULE_DESCRIPTION("FIMC-IS SPI driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,177 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_SUBDEV_H
#define FIMC_IS_SUBDEV_H
#include "fimc-is-video.h"
struct fimc_is_device_sensor;
struct fimc_is_device_ischain;
struct fimc_is_groupmgr;
struct fimc_is_group;
enum fimc_is_subdev_device_type {
FIMC_IS_SENSOR_SUBDEV,
FIMC_IS_ISCHAIN_SUBDEV,
};
enum fimc_is_subdev_state {
FIMC_IS_SUBDEV_OPEN,
FIMC_IS_SUBDEV_START,
FIMC_IS_SUBDEV_RUN,
FIMC_IS_SUBDEV_FORCE_SET
};
struct fimc_is_subdev_path {
u32 width;
u32 height;
struct fimc_is_crop canv;
struct fimc_is_crop crop;
};
enum fimc_is_subdev_id {
ENTRY_SENSOR,
ENTRY_SSVC0,
ENTRY_SSVC1,
ENTRY_SSVC2,
ENTRY_SSVC3,
ENTRY_BNS,
ENTRY_3AA,
ENTRY_3AC,
ENTRY_3AP,
ENTRY_ISP,
ENTRY_IXC,
ENTRY_IXP,
ENTRY_DRC,
ENTRY_DIS,
ENTRY_ODC,
ENTRY_DNR,
ENTRY_SCC,
ENTRY_SCP,
ENTRY_MCS,
ENTRY_M0P,
ENTRY_M1P,
ENTRY_M2P,
ENTRY_M3P,
ENTRY_M4P,
ENTRY_VRA,
ENTRY_END
};
struct fimc_is_subdev_ops {
int (*bypass)(struct fimc_is_subdev *subdev,
void *device_data,
struct fimc_is_frame *frame,
bool bypass);
int (*cfg)(struct fimc_is_subdev *subdev,
void *device_data,
struct fimc_is_frame *frame,
struct fimc_is_crop *incrop,
struct fimc_is_crop *otcrop,
u32 *lindex,
u32 *hindex,
u32 *indexes);
int (*tag)(struct fimc_is_subdev *subdev,
void *device_data,
struct fimc_is_frame *frame,
struct camera2_node *node);
};
struct fimc_is_subdev {
u32 id;
u32 vid; /* video id */
u32 cid; /* capture node id */
char name[4];
u32 instance;
unsigned long state;
u32 constraints_width; /* spec in width */
u32 constraints_height; /* spec in height */
u32 param_otf_in;
u32 param_dma_in;
u32 param_otf_ot;
u32 param_dma_ot;
struct fimc_is_subdev_path input;
struct fimc_is_subdev_path output;
struct fimc_is_video_ctx *vctx;
struct fimc_is_subdev *leader;
const struct fimc_is_subdev_ops *ops;
};
int fimc_is_sensor_subdev_open(struct fimc_is_device_sensor *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_sensor_subdev_close(struct fimc_is_device_sensor *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_subdev_open(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
int fimc_is_ischain_subdev_close(struct fimc_is_device_ischain *device,
struct fimc_is_video_ctx *vctx);
/*common subdev*/
int fimc_is_subdev_probe(struct fimc_is_subdev *subdev,
u32 instance,
u32 id,
char *name,
const struct fimc_is_subdev_ops *sops);
int fimc_is_subdev_open(struct fimc_is_subdev *subdev,
struct fimc_is_video_ctx *vctx,
void *ctl_data);
int fimc_is_subdev_close(struct fimc_is_subdev *subdev);
int fimc_is_subdev_reqbuf(struct fimc_is_subdev *subdev);
int fimc_is_subdev_buffer_queue(struct fimc_is_subdev *subdev, u32 index);
int fimc_is_subdev_buffer_finish(struct fimc_is_subdev *subdev, u32 index);
void fimc_is_subdev_dis_start(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes);
void fimc_is_subdev_dis_stop(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes);
void fimc_is_subdev_dis_bypass(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes, bool bypass);
void fimc_is_subdev_dnr_start(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes);
void fimc_is_subdev_dnr_stop(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes);
void fimc_is_subdev_dnr_bypass(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes, bool bypass);
void fimc_is_subdev_drc_start(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes);
void fimc_is_subdev_drc_stop(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes);
void fimc_is_subdev_drc_bypass(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes, bool bypass);
void fimc_is_subdev_odc_start(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes);
void fimc_is_subdev_odc_stop(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes);
void fimc_is_subdev_odc_bypass(struct fimc_is_device_ischain *device,
struct fimc_is_frame *frame, u32 *lindex, u32 *hindex, u32 *indexes, bool bypass);
int fimc_is_vra_trigger(struct fimc_is_device_ischain *device,
struct fimc_is_subdev *subdev,
struct fimc_is_frame *frame);
int fimc_is_sensor_subdev_reqbuf(void *qdevice,
struct fimc_is_queue *queue, u32 count);
struct fimc_is_subdev * video2subdev(enum fimc_is_subdev_device_type device_type,
void *device, u32 vid);
#define GET_SUBDEV_FRAMEMGR(subdev) \
(((subdev) && (subdev)->vctx) ? (&(subdev)->vctx->queue.framemgr) : NULL)
#define GET_SUBDEV_QUEUE(subdev) \
(((subdev) && (subdev)->vctx) ? (&(subdev)->vctx->queue) : NULL)
#define CALL_SOPS(s, op, args...) (((s) && (s)->ops && (s)->ops->op) ? ((s)->ops->op(s, args)) : 0)
#endif

View file

@ -0,0 +1,343 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/time.h>
#include "fimc-is-time.h"
#include "fimc-is-groupmgr.h"
#include "fimc-is-device-sensor.h"
#include "fimc-is-device-ischain.h"
static struct timeval itime[10];
#define JITTER_CNT 50
static u64 jitter_array[JITTER_CNT];
static u64 jitter_prio;
static u64 jitter_cnt = 0;
void TIME_STR(unsigned int index)
{
do_gettimeofday(&itime[index]);
}
void TIME_END(unsigned int index, const char *name)
{
u32 time;
struct timeval temp;
do_gettimeofday(&temp);
time = (temp.tv_sec - itime[index].tv_sec)*1000000 +
(temp.tv_usec - itime[index].tv_usec);
info("TIME_MEASURE(%s) : %dus\n", name, time);
}
void fimc_is_jitter(u64 timestamp)
{
if (jitter_cnt == 0) {
jitter_prio = timestamp;
jitter_cnt++;
return;
}
jitter_array[jitter_cnt-1] = timestamp - jitter_prio;
jitter_prio = timestamp;
if (jitter_cnt >= JITTER_CNT) {
u64 i, variance, tot = 0, square_tot = 0, avg = 0, square_avg = 0;;
for (i = 0; i < JITTER_CNT; ++i) {
tot += jitter_array[i];
square_tot += (jitter_array[i] * jitter_array[i]);
}
avg = tot / JITTER_CNT;
square_avg = square_tot / JITTER_CNT;
variance = square_avg - (avg * avg);
info("[TIM] variance : %lld, average : %lld\n", variance, avg);
jitter_cnt = 0;
} else {
jitter_cnt++;
}
}
u64 fimc_is_get_timestamp(void)
{
struct timespec curtime;
do_posix_clock_monotonic_gettime(&curtime);
return (u64)curtime.tv_sec*1000000000 + curtime.tv_nsec;
}
u64 fimc_is_get_timestamp_boot(void)
{
struct timespec curtime;
curtime = ktime_to_timespec(ktime_get_boottime());
return (u64)curtime.tv_sec*1000000000 + curtime.tv_nsec;
}
static inline u32 fimc_is_get_time(struct timeval *str, struct timeval *end)
{
return (end->tv_sec - str->tv_sec)*1000000 + (end->tv_usec - str->tv_usec);
}
#ifdef MEASURE_TIME
#ifdef MONITOR_TIME
void monitor_init(struct fimc_is_time *time)
{
time->time_count = 0;
time->time1_min = 0;
time->time1_max = 0;
time->time1_tot = 0;
time->time2_min = 0;
time->time2_max = 0;
time->time2_tot = 0;
time->time3_min = 0;
time->time3_max = 0;
time->time3_tot = 0;
time->time4_cur = 0;
time->time4_old = 0;
time->time4_tot = 0;
}
void monitor_period(struct fimc_is_time *time,
u32 report_period)
{
time->report_period = report_period;
}
static void monitor_report(void *group_data,
void *frame_data)
{
u32 index, shotindex;
u32 fduration, ctime, dtime;
u32 temp1, temp2, temp3, temp4, temp;
struct fimc_is_monitor *mp;
struct fimc_is_device_ischain *device;
struct fimc_is_group *group;
struct fimc_is_frame *frame;
struct fimc_is_time *time;
bool valid = true;
BUG_ON(!group_data);
BUG_ON(!frame_data);
group = group_data;
device = group->device;
time = &group->time;
frame = frame_data;
mp = frame->mpoint;
fduration = (1000000 / fimc_is_sensor_g_framerate(device->sensor)) + MONITOR_TIMEOUT;
ctime = 15000;
dtime = 15000;
/* Q interval */
if (!frame->result && mp[TMM_QUEUE].check && mp[TMM_START].check) {
temp1 = fimc_is_get_time(&mp[TMM_QUEUE].time, &mp[TMM_START].time);
if (temp1 > fduration) {
mgrinfo("[TIM] Q(%dus > %dus)\n", device, group, frame, temp1, fduration);
}
} else {
valid = false;
}
/* Callback interval */
if (!frame->result && mp[TMM_SCALL].check && mp[TMM_ECALL].check) {
temp2 = fimc_is_get_time(&mp[TMM_SCALL].time, &mp[TMM_ECALL].time);
if (temp2 > ctime) {
mgrinfo("[TIM] C(%dus > %dus)\n", device, group, frame, temp2, ctime);
for (index = TMM_SCALL; index < TMM_ECALL; ++index) {
temp = fimc_is_get_time(&mp[index].time, &mp[index + 1].time);
mgrinfo("[TIM] %02d-%02d(%dus)\n", device, group, frame, index, index + 1, temp);
}
}
} else {
valid = false;
}
/* Shot interval */
if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state)) {
shotindex = TMM_SHOT2;
} else {
shotindex = TMM_SHOT1;
}
if (!frame->result && mp[shotindex].check && mp[TMM_SDONE].check) {
temp3 = fimc_is_get_time(&mp[shotindex].time, &mp[TMM_SDONE].time);
if (temp3 > fduration) {
mgrinfo("[TIM] S(%dus > %dus)\n", device, group, frame, temp3, fduration);
}
} else {
valid = false;
}
/* Deque interval */
if (!frame->result && mp[TMM_SDONE].check && mp[TMM_DEQUE].check) {
temp4 = fimc_is_get_time(&mp[TMM_SDONE].time, &mp[TMM_DEQUE].time);
if (temp4 > dtime) {
mgrinfo("[TIM] D(%dus > %dus)\n", device, group, frame, temp4, dtime);
for (index = TMM_SDONE; index < TMM_DEQUE; ++index) {
temp = fimc_is_get_time(&mp[index].time, &mp[index + 1].time);
mgrinfo("[TIM] %02d-%02d(%dus)\n", device, group, frame, index, index + 1, temp);
}
}
} else {
valid = false;
}
for (index = 0; index < TMM_END; ++index)
mp[index].check = false;
if (!valid)
return;
#ifdef MONITOR_REPORT
temp1 += temp2;
temp2 = temp3;
temp3 = temp4;
if (!time->time_count) {
time->time1_min = temp1;
time->time1_max = temp1;
time->time2_min = temp2;
time->time2_max = temp2;
time->time3_min = temp3;
time->time3_max = temp3;
} else {
if (time->time1_min > temp1)
time->time1_min = temp1;
if (time->time1_max < temp1)
time->time1_max = temp1;
if (time->time2_min > temp2)
time->time2_min = temp2;
if (time->time2_max < temp2)
time->time2_max = temp2;
if (time->time3_min > temp3)
time->time3_min = temp3;
if (time->time3_max < temp3)
time->time3_max = temp3;
}
time->time1_tot += temp1;
time->time2_tot += temp2;
time->time3_tot += temp3;
time->time4_cur = mp[TMM_QUEUE].time.tv_sec * 1000000 + mp[TMM_QUEUE].time.tv_usec;
time->time4_tot += (time->time4_cur - time->time4_old);
time->time4_old = time->time4_cur;
time->time_count++;
if (time->time_count % time->report_period)
return;
mginfo("[TIM] Q(%05d,%05d,%05d), S(%05d,%05d,%05d), D(%05d,%05d,%05d) : %d(%dfps)\n",
device, group,
temp1, time->time1_max, time->time1_tot / time->time_count,
temp2, time->time2_max, time->time2_tot / time->time_count,
temp3, time->time3_max, time->time3_tot / time->time_count,
time->time4_tot / time->report_period,
(1000000 * time->report_period) / time->time4_tot);
time->time_count = 0;
time->time1_tot = 0;
time->time2_tot = 0;
time->time3_tot = 0;
time->time4_tot = 0;
#endif
}
#endif
#ifdef INTERFACE_TIME
void measure_init(struct fimc_is_interface_time *time, u32 cmd)
{
time->cmd = cmd;
time->time_max = 0;
time->time_min = 0;
time->time_tot = 0;
time->time_cnt = 0;
}
void measure_time(struct fimc_is_interface_time *time,
u32 instance,
u32 group,
struct timeval *start,
struct timeval *end)
{
u32 temp;
temp = (end->tv_sec - start->tv_sec)*1000000 + (end->tv_usec - start->tv_usec);
if (time->time_cnt) {
time->time_max = temp;
time->time_min = temp;
} else {
if (time->time_min > temp)
time->time_min = temp;
if (time->time_max < temp)
time->time_max = temp;
}
time->time_tot += temp;
time->time_cnt++;
pr_info("cmd[%d][%d](%d) : curr(%d), max(%d), avg(%d)\n",
instance, group, time->cmd, temp, time->time_max, time->time_tot / time->time_cnt);
}
#endif
#endif
void monitor_point(void *group_data,
void *frame_data,
u32 mpoint)
{
struct fimc_is_group *group;
BUG_ON(!group_data);
BUG_ON(!frame_data);
group = group_data;
#if defined(MEASURE_TIME) && defined(MONITOR_TIME)
{
struct fimc_is_frame *frame;
struct fimc_is_monitor *point;
frame = frame_data;
point = &frame->mpoint[mpoint];
do_gettimeofday(&point->time);
/* only group thread postion */
if (mpoint >= 2 && mpoint <= 12)
group->pcount = mpoint;
point->pcount = mpoint;
point->check = true;
if (mpoint == TMM_DEQUE)
monitor_report(group_data, frame_data);
}
#else
group->pcount = mpoint;
#endif
}

View file

@ -0,0 +1,97 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FIMC_IS_TIME_H
#define FIMC_IS_TIME_H
#include <linux/time.h>
/* #define MEASURE_TIME */
/* #define MONITOR_TIME */
#define MONITOR_TIMEOUT 5000 /* 5ms */
/* #define MONITOR_REPORT */
/* #define EXTERNAL_TIME */
/* #define INTERFACE_TIME */
#define INSTANCE_MASK 0x3
#define TM_FLITE_STR 0
#define TM_FLITE_END 1
#define TM_SHOT 2
#define TM_SHOT_D 3
#define TM_META_D 4
#define TM_MAX_INDEX 5
#define TMM_QUEUE 0
#define TMM_START 1
#define TMM_SCALL 7
#define TMM_SHOT1 11
#define TMM_ECALL 12
#define TMM_SDONE 13
#define TMM_DEQUE 15
#define TMM_SHOT2 16
#define TMM_END 17
struct fimc_is_monitor {
struct timeval time;
u32 pcount;
bool check;
};
struct fimc_is_time {
u32 report_period;
u32 time_count;
u32 time1_min;
u32 time1_max;
u32 time1_tot;
u32 time2_min;
u32 time2_max;
u32 time2_tot;
u32 time3_min;
u32 time3_max;
u32 time3_tot;
u32 time4_cur;
u32 time4_old;
u32 time4_tot;
};
struct fimc_is_interface_time {
u32 cmd;
u32 time_tot;
u32 time_min;
u32 time_max;
u32 time_cnt;
};
void TIME_STR(unsigned int index);
void TIME_END(unsigned int index, const char *name);
void fimc_is_jitter(u64 timestamp);
u64 fimc_is_get_timestamp(void);
u64 fimc_is_get_timestamp_boot(void);
#define PROGRAM_COUNT(count) monitor_point(group, frame, count)
void monitor_point(void *group_data, void *frame_data, u32 mpoint);
#ifdef MEASURE_TIME
#ifdef MONITOR_TIME
void monitor_init(struct fimc_is_time *time);
void monitor_period(struct fimc_is_time *time, u32 report_period);
#endif
#ifdef INTERFACE_TIME
void measure_init(struct fimc_is_interface_time *time, u32 cmd);
void measure_time(struct fimc_is_interface_time *time,
u32 instance,
u32 group,
struct timeval *start,
struct timeval *end);
#endif
#endif
#endif

View file

@ -0,0 +1,841 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include <media/videobuf2-core.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mediabus.h>
#include <media/exynos_mc.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
#include "fimc-is-param.h"
const struct v4l2_file_operations fimc_is_3aa_video_fops;
const struct v4l2_ioctl_ops fimc_is_3aa_video_ioctl_ops;
const struct vb2_ops fimc_is_3aa_qops;
int fimc_is_30s_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_30s;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_3XS_NAME(0),
FIMC_IS_VIDEO_30S_NUM,
VFL_DIR_M2M,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_3aa_video_fops,
&fimc_is_3aa_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
int fimc_is_31s_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_31s;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_3XS_NAME(1),
FIMC_IS_VIDEO_31S_NUM,
VFL_DIR_M2M,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_3aa_video_fops,
&fimc_is_3aa_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
/*
* =============================================================================
* Video File Opertation
* =============================================================================
*/
static int fimc_is_3aa_video_open(struct file *file)
{
int ret = 0;
struct fimc_is_video *video;
struct fimc_is_video_ctx *vctx;
struct fimc_is_device_ischain *device;
struct fimc_is_resourcemgr *resourcemgr;
vctx = NULL;
device = NULL;
video = video_drvdata(file);
resourcemgr = video->resourcemgr;
if (!resourcemgr) {
err("resourcemgr is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_resource_open(resourcemgr, RESOURCE_TYPE_ISCHAIN, (void **)&device);
if (ret) {
err("fimc_is_resource_open is fail(%d)", ret);
goto p_err;
}
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
minfo("[3%dS:V] %s\n", device, GET_3XS_ID(video), __func__);
ret = open_vctx(file, video, &vctx, device->instance, FRAMEMGR_ID_3XS);
if (ret) {
merr("open_vctx is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_video_open(vctx,
device,
VIDEO_3XS_READY_BUFFERS,
video,
&fimc_is_3aa_qops,
&fimc_is_ischain_3aa_ops);
if (ret) {
merr("fimc_is_video_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
ret = fimc_is_ischain_3aa_open(device, vctx);
if (ret) {
merr("fimc_is_ischain_3aa_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3aa_video_close(struct file *file)
{
int ret = 0;
int refcount;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_video *video;
struct fimc_is_device_ischain *device;
BUG_ON(!file);
BUG_ON(!vctx);
BUG_ON(!GET_VIDEO(vctx));
BUG_ON(!GET_DEVICE(vctx));
video = GET_VIDEO(vctx);
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_3aa_close(device, vctx);
if (ret)
merr("fimc_is_ischain_3aa_close is fail(%d)", device, ret);
ret = fimc_is_video_close(vctx);
if (ret)
merr("fimc_is_video_close is fail(%d)", device, ret);
refcount = close_vctx(file, video, vctx);
if (refcount < 0)
merr("close_vctx is fail(%d)", device, refcount);
minfo("[3%dS:V] %s(%d,%d):%d\n", device, GET_3XS_ID(video), __func__, atomic_read(&device->open_cnt), refcount, ret);
return ret;
}
static unsigned int fimc_is_3aa_video_poll(struct file *file,
struct poll_table_struct *wait)
{
u32 ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_poll(file, vctx, wait);
if (ret)
merr("fimc_is_video_poll is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3aa_video_mmap(struct file *file,
struct vm_area_struct *vma)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_mmap(file, vctx, vma);
if (ret)
merr("fimc_is_video_mmap is fail(%d)", vctx, ret);
return ret;
}
const struct v4l2_file_operations fimc_is_3aa_video_fops = {
.owner = THIS_MODULE,
.open = fimc_is_3aa_video_open,
.release = fimc_is_3aa_video_close,
.poll = fimc_is_3aa_video_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = fimc_is_3aa_video_mmap,
};
/*
* =============================================================================
* Video Ioctl Opertation
* =============================================================================
*/
static int fimc_is_3aa_video_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
/* Todo : add to query capability code */
return 0;
}
static int fimc_is_3aa_video_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
/* Todo : add to enumerate format code */
return 0;
}
static int fimc_is_3aa_video_get_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
/* Todo : add to get format code */
return 0;
}
static int fimc_is_3aa_video_set_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!format);
mdbgv_3aa("%s\n", vctx, __func__);
ret = fimc_is_video_set_format_mplane(file, vctx, format);
if (ret) {
merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3aa_video_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cropcap)
{
/* Todo : add to crop capability code */
return 0;
}
static int fimc_is_3aa_video_get_crop(struct file *file, void *fh,
struct v4l2_crop *crop)
{
/* Todo : add to get crop control code */
return 0;
}
static int fimc_is_3aa_video_set_crop(struct file *file, void *fh,
const struct v4l2_crop *crop)
{
/* Todo : add to set crop control code */
return 0;
}
static int fimc_is_3aa_video_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
mdbgv_3aa("%s(buffers : %d)\n", vctx, __func__, buf->count);
ret = fimc_is_video_reqbufs(file, vctx, buf);
if (ret)
merr("fimc_is_video_reqbufs is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3aa_video_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3aa("%s\n", vctx, __func__);
ret = fimc_is_video_querybuf(file, vctx, buf);
if (ret)
merr("fimc_is_video_querybuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3aa_video_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
#ifdef DBG_STREAMING
mdbgv_3aa("%s(%02d:%d)\n", vctx, __func__, buf->type, buf->index);
#endif
ret = CALL_VOPS(vctx, qbuf, buf);
if (ret)
merr("qbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3aa_video_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
bool blocking = file->f_flags & O_NONBLOCK;
BUG_ON(!vctx);
#ifdef DBG_STREAMING
mdbgv_3aa("%s\n", vctx, __func__);
#endif
ret = CALL_VOPS(vctx, dqbuf, buf, blocking);
if (ret)
merr("dqbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3aa_video_prepare(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
BUG_ON(!buf);
BUG_ON(!vctx);
BUG_ON(!GET_FRAMEMGR(vctx));
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
#ifdef DBG_STREAMING
mdbgv_3aa("%s\n", vctx, __func__);
#endif
device = GET_DEVICE(vctx);
framemgr = GET_FRAMEMGR(vctx);
frame = &framemgr->frames[buf->index];
ret = fimc_is_video_prepare(file, vctx, buf);
if (ret) {
merr("fimc_is_video_prepare is fail(%d)", vctx, ret);
goto p_err;
}
if (!test_bit(FRAME_MEM_MAPPED, &frame->mem_state)) {
fimc_is_itf_map(device, GROUP_ID(device->group_3aa.id), frame->dvaddr_shot, frame->shot_size);
set_bit(FRAME_MEM_MAPPED, &frame->mem_state);
}
p_err:
minfo("[3%dS:V] %s(%d):%d\n", device, GET_3XS_ID(GET_VIDEO(vctx)), __func__, buf->index, ret);
return ret;
}
static int fimc_is_3aa_video_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3aa("%s\n", vctx, __func__);
ret = fimc_is_video_streamon(file, vctx, type);
if (ret)
merr("fimc_is_video_streamon is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3aa_video_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3aa("%s\n", vctx, __func__);
ret = fimc_is_video_streamoff(file, vctx, type);
if (ret)
merr("fimc_is_video_streamoff is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3aa_video_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
/* Todo: add enum input control code */
return 0;
}
static int fimc_is_3aa_video_g_input(struct file *file, void *priv,
unsigned int *input)
{
/* Todo: add to get input control code */
return 0;
}
static int fimc_is_3aa_video_s_input(struct file *file, void *priv,
unsigned int input)
{
int ret = 0;
u32 stream, module, vindex, intype, leader;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!vctx->device);
device = GET_DEVICE(vctx);
stream = (input & INPUT_STREAM_MASK) >> INPUT_STREAM_SHIFT;
module = (input & INPUT_MODULE_MASK) >> INPUT_MODULE_SHIFT;
vindex = (input & INPUT_VINDEX_MASK) >> INPUT_VINDEX_SHIFT;
intype = (input & INPUT_INTYPE_MASK) >> INPUT_INTYPE_SHIFT;
leader = (input & INPUT_LEADER_MASK) >> INPUT_LEADER_SHIFT;
mdbgv_3aa("%s(input : %08X)[%d,%d,%d,%d,%d]\n", vctx, __func__, input,
stream, module, vindex, intype, leader);
ret = fimc_is_video_s_input(file, vctx);
if (ret) {
merr("fimc_is_video_s_input is fail(%d)", vctx, ret);
goto p_err;
}
ret = fimc_is_ischain_3aa_s_input(device, stream, module, vindex, intype, leader);
if (ret) {
merr("fimc_is_ischain_3aa_s_input is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3aa_video_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
unsigned int value = 0;
unsigned int captureIntent = 0;
unsigned int captureCount = 0;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!ctrl);
mdbgv_3aa("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
switch (ctrl->id) {
case V4L2_CID_IS_INTENT:
value = (unsigned int)ctrl->value;
captureIntent = (value >> 16) & 0x0000FFFF;
if (captureIntent == AA_CAPTRUE_INTENT_STILL_CAPTURE_OIS_DYNAMIC_SHOT) {
captureCount = value & 0x0000FFFF;
} else {
captureIntent = ctrl->value;
captureCount = 0;
}
device->group_3aa.intent_ctl.captureIntent = captureIntent;
device->group_3aa.intent_ctl.vendor_captureCount = captureCount;
minfo("[3AA:V] s_ctrl intent(%d) count(%d)\n", vctx, captureIntent, captureCount);
break;
case V4L2_CID_IS_FORCE_DONE:
set_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &device->group_3aa.state);
break;
default:
ret = fimc_is_video_s_ctrl(file, vctx, ctrl);
if (ret) {
merr("fimc_is_video_s_ctrl is fail(%d)", device, ret);
goto p_err;
}
break;
}
p_err:
return ret;
}
static int fimc_is_3aa_video_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
/* Todo: add to get control code */
return 0;
}
static int fimc_is_3aa_video_s_ext_ctrl(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
int ret = 0;
int i;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_framemgr *framemgr;
struct fimc_is_queue *queue;
struct v4l2_ext_control *ext_ctrl;
struct v4l2_control ctrl;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!ctrls);
mdbgv_3aa("%s\n", vctx, __func__);
if (ctrls->ctrl_class != V4L2_CTRL_CLASS_CAMERA) {
merr("Invalid control class(%d)", vctx, ctrls->ctrl_class);
ret = -EINVAL;
goto p_err;
}
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
framemgr = &queue->framemgr;
for (i = 0; i < ctrls->count; i++) {
ext_ctrl = (ctrls->controls + i);
switch (ext_ctrl->id) {
#ifdef ENABLE_ULTRA_FAST_SHOT
case V4L2_CID_IS_FAST_CAPTURE_CONTROL:
{
struct fast_ctl_capture *fast_capture =
(struct fast_ctl_capture *)&device->is_region->fast_ctl.fast_capture;
ret = copy_from_user(fast_capture, ext_ctrl->ptr, sizeof(struct fast_ctl_capture));
if (ret) {
merr("copy_from_user is fail(%d)", vctx, ret);
goto p_err;
}
fast_capture->ready = 1;
device->fastctlmgr.fast_capture_count = 2;
vb2_ion_sync_for_device(
device->imemory.fw_cookie,
(ulong)fast_capture - device->imemory.kvaddr,
sizeof(struct fast_ctl_capture),
DMA_TO_DEVICE);
mvinfo("Fast capture control(Intent:%d, count:%d, exposureTime:%d)\n",
vctx, vctx->video, fast_capture->capture_intent, fast_capture->capture_count,
fast_capture->capture_exposureTime);
}
break;
#endif
default:
ctrl.id = ext_ctrl->id;
ctrl.value = ext_ctrl->value;
ret = fimc_is_video_s_ctrl(file, vctx, &ctrl);
if (ret) {
merr("fimc_is_video_s_ctrl is fail(%d)", device, ret);
goto p_err;
}
break;
}
}
p_err:
return ret;
}
static int fimc_is_3aa_video_g_ext_ctrl(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
/* Todo: add to get extra control code */
return 0;
}
const struct v4l2_ioctl_ops fimc_is_3aa_video_ioctl_ops = {
.vidioc_querycap = fimc_is_3aa_video_querycap,
.vidioc_enum_fmt_vid_out_mplane = fimc_is_3aa_video_enum_fmt_mplane,
.vidioc_enum_fmt_vid_cap_mplane = fimc_is_3aa_video_enum_fmt_mplane,
.vidioc_g_fmt_vid_out_mplane = fimc_is_3aa_video_get_format_mplane,
.vidioc_g_fmt_vid_cap_mplane = fimc_is_3aa_video_get_format_mplane,
.vidioc_s_fmt_vid_out_mplane = fimc_is_3aa_video_set_format_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_is_3aa_video_set_format_mplane,
.vidioc_querybuf = fimc_is_3aa_video_querybuf,
.vidioc_reqbufs = fimc_is_3aa_video_reqbufs,
.vidioc_qbuf = fimc_is_3aa_video_qbuf,
.vidioc_dqbuf = fimc_is_3aa_video_dqbuf,
.vidioc_prepare_buf = fimc_is_3aa_video_prepare,
.vidioc_streamon = fimc_is_3aa_video_streamon,
.vidioc_streamoff = fimc_is_3aa_video_streamoff,
.vidioc_enum_input = fimc_is_3aa_video_enum_input,
.vidioc_g_input = fimc_is_3aa_video_g_input,
.vidioc_s_input = fimc_is_3aa_video_s_input,
.vidioc_s_ctrl = fimc_is_3aa_video_s_ctrl,
.vidioc_g_ctrl = fimc_is_3aa_video_g_ctrl,
.vidioc_s_ext_ctrls = fimc_is_3aa_video_s_ext_ctrl,
.vidioc_g_ext_ctrls = fimc_is_3aa_video_g_ext_ctrl,
.vidioc_cropcap = fimc_is_3aa_video_cropcap,
.vidioc_g_crop = fimc_is_3aa_video_get_crop,
.vidioc_s_crop = fimc_is_3aa_video_set_crop,
};
static int fimc_is_3aa_queue_setup(struct vb2_queue *vbq,
const struct v4l2_format *fmt,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
void *allocators[])
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_video *video;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
mdbgv_3aa("%s\n", vctx, __func__);
video = GET_VIDEO(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_setup(queue,
video->alloc_ctx,
num_planes,
sizes,
allocators);
if (ret)
merr("fimc_is_queue_setup is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3aa_buffer_prepare(struct vb2_buffer *vb)
{
return fimc_is_queue_prepare(vb);
}
static inline void fimc_is_3aa_wait_prepare(struct vb2_queue *vbq)
{
fimc_is_queue_wait_prepare(vbq);
}
static inline void fimc_is_3aa_wait_finish(struct vb2_queue *vbq)
{
fimc_is_queue_wait_finish(vbq);
}
static int fimc_is_3aa_start_streaming(struct vb2_queue *vbq,
unsigned int count)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_3aa("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_start_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_start_streaming is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static void fimc_is_3aa_stop_streaming(struct vb2_queue *vbq)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_3aa("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_stop_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_stop_streaming is fail(%d)", device, ret);
return;
}
}
static void fimc_is_3aa_buffer_queue(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_device_ischain *device;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_3aa("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_buffer_queue(queue, vb);
if (ret) {
merr("fimc_is_queue_buffer_queue is fail(%d)", device, ret);
return;
}
ret = fimc_is_ischain_3aa_buffer_queue(device, queue, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_ischain_3aa_buffer_queue is fail(%d)", device, ret);
return;
}
}
static void fimc_is_3aa_buffer_finish(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_3aa("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_3aa_buffer_finish(device, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_ischain_3aa_buffer_finish is fail(%d)", device, ret);
return;
}
}
const struct vb2_ops fimc_is_3aa_qops = {
.queue_setup = fimc_is_3aa_queue_setup,
.buf_init = fimc_is_buffer_init,
.buf_prepare = fimc_is_3aa_buffer_prepare,
.buf_queue = fimc_is_3aa_buffer_queue,
.buf_finish = fimc_is_3aa_buffer_finish,
.wait_prepare = fimc_is_3aa_wait_prepare,
.wait_finish = fimc_is_3aa_wait_finish,
.start_streaming = fimc_is_3aa_start_streaming,
.stop_streaming = fimc_is_3aa_stop_streaming,
};

View file

@ -0,0 +1,740 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
const struct v4l2_file_operations fimc_is_3xc_video_fops;
const struct v4l2_ioctl_ops fimc_is_3xc_video_ioctl_ops;
const struct vb2_ops fimc_is_3xc_qops;
int fimc_is_30c_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_30c;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_3XC_NAME(0),
FIMC_IS_VIDEO_30C_NUM,
VFL_DIR_RX,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_3xc_video_fops,
&fimc_is_3xc_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
int fimc_is_31c_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_31c;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_3XC_NAME(1),
FIMC_IS_VIDEO_31C_NUM,
VFL_DIR_RX,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_3xc_video_fops,
&fimc_is_3xc_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
/*
* =============================================================================
* Video File Opertation
* =============================================================================
*/
static int fimc_is_3xc_video_open(struct file *file)
{
int ret = 0;
struct fimc_is_video *video;
struct fimc_is_video_ctx *vctx;
struct fimc_is_device_ischain *device;
struct fimc_is_resourcemgr *resourcemgr;
vctx = NULL;
device = NULL;
video = video_drvdata(file);
resourcemgr = video->resourcemgr;
if (!resourcemgr) {
err("resourcemgr is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_resource_open(resourcemgr, RESOURCE_TYPE_ISCHAIN, (void **)&device);
if (ret) {
err("fimc_is_resource_open is fail(%d)", ret);
goto p_err;
}
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
minfo("[3%dC:V] %s\n", device, GET_3XC_ID(video), __func__);
ret = open_vctx(file, video, &vctx, device->instance, FRAMEMGR_ID_3XC);
if (ret) {
merr("open_vctx is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_video_open(vctx,
device,
VIDEO_3XC_READY_BUFFERS,
video,
&fimc_is_3xc_qops,
&fimc_is_ischain_subdev_ops);
if (ret) {
merr("fimc_is_video_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
ret = fimc_is_ischain_subdev_open(device, vctx);
if (ret) {
merr("fimc_is_ischain_subdev_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3xc_video_close(struct file *file)
{
int ret = 0;
int refcount;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_video *video;
struct fimc_is_device_ischain *device;
BUG_ON(!file);
BUG_ON(!vctx);
BUG_ON(!GET_VIDEO(vctx));
BUG_ON(!GET_DEVICE(vctx));
video = GET_VIDEO(vctx);
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_subdev_close(device, vctx);
if (ret)
merr("fimc_is_subdev_close is fail(%d)", device, ret);
ret = fimc_is_video_close(vctx);
if (ret)
merr("fimc_is_video_close is fail(%d)", device, ret);
refcount = close_vctx(file, video, vctx);
if (refcount < 0)
merr("close_vctx is fail(%d)", device, refcount);
minfo("[3%dC:V] %s(%d,%d):%d\n", device, GET_3XC_ID(video), __func__, atomic_read(&device->open_cnt), refcount, ret);
return ret;
}
static unsigned int fimc_is_3xc_video_poll(struct file *file,
struct poll_table_struct *wait)
{
u32 ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_poll(file, vctx, wait);
if (ret)
merr("fimc_is_video_poll is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xc_video_mmap(struct file *file,
struct vm_area_struct *vma)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_mmap(file, vctx, vma);
if (ret)
merr("fimc_is_video_mmap is fail(%d)", vctx, ret);
return ret;
}
const struct v4l2_file_operations fimc_is_3xc_video_fops = {
.owner = THIS_MODULE,
.open = fimc_is_3xc_video_open,
.release = fimc_is_3xc_video_close,
.poll = fimc_is_3xc_video_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = fimc_is_3xc_video_mmap,
};
/*
* =============================================================================
* Video Ioctl Opertation
* =============================================================================
*/
static int fimc_is_3xc_video_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
/* Todo : add to query capability code */
return 0;
}
static int fimc_is_3xc_video_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xc_video_get_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xc_video_set_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!format);
mdbgv_3xc("%s\n", vctx, __func__);
ret = fimc_is_video_set_format_mplane(file, vctx, format);
if (ret) {
merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3xc_video_try_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xc_video_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cropcap)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xc_video_get_crop(struct file *file, void *fh,
struct v4l2_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xc_video_set_crop(struct file *file, void *fh,
const struct v4l2_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xc_video_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *leader, *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
mdbgv_3xc("%s(buffers : %d)\n", vctx, __func__, buf->count);
device = GET_DEVICE(vctx);
subdev = &device->txc;
leader = subdev->leader;
if (leader && test_bit(FIMC_IS_SUBDEV_START, &leader->state)) {
err("leader%d still running, subdev%d req is not applied", leader->id, subdev->id);
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_reqbufs(file, vctx, buf);
if (ret) {
merr("fimc_is_video_reqbufs is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3xc_video_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3xc("%s\n", vctx, __func__);
ret = fimc_is_video_querybuf(file, vctx, buf);
if (ret)
merr("fimc_is_video_querybuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xc_video_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
#ifdef DBG_STREAMING
mdbgv_3xc("%s(index : %d)\n", vctx, __func__, buf->index);
#endif
ret = CALL_VOPS(vctx, qbuf, buf);
if (ret)
merr("qbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xc_video_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
bool blocking = file->f_flags & O_NONBLOCK;
#ifdef DBG_STREAMING
mdbgv_3xc("%s\n", vctx, __func__);
#endif
ret = CALL_VOPS(vctx, dqbuf, buf, blocking);
if (ret)
merr("dqbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xc_video_prepare(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
BUG_ON(!buf);
BUG_ON(!vctx);
BUG_ON(!GET_FRAMEMGR(vctx));
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
#ifdef DBG_STREAMING
mdbgv_3xc("%s\n", vctx, __func__);
#endif
device = GET_DEVICE(vctx);
framemgr = GET_FRAMEMGR(vctx);
frame = &framemgr->frames[buf->index];
ret = fimc_is_video_prepare(file, vctx, buf);
if (ret) {
merr("fimc_is_video_prepare is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
minfo("[3%dC:V] %s(%d):%d\n", device, GET_3XC_ID(GET_VIDEO(vctx)), __func__, buf->index, ret);
return ret;
}
static int fimc_is_3xc_video_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3xc("%s\n", vctx, __func__);
ret = fimc_is_video_streamon(file, vctx, type);
if (ret)
merr("fimc_is_video_streamon is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xc_video_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3xc("%s\n", vctx, __func__);
ret = fimc_is_video_streamoff(file, vctx, type);
if (ret)
merr("fimc_is_video_streamoff is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xc_video_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
/* Todo: add enum input control code */
return 0;
}
static int fimc_is_3xc_video_g_input(struct file *file, void *priv,
unsigned int *input)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xc_video_s_input(struct file *file, void *priv,
unsigned int input)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
ret = fimc_is_video_s_input(file, vctx);
if (ret) {
merr("fimc_is_video_s_input is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3xc_video_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_framemgr *framemgr;
BUG_ON(!vctx);
BUG_ON(!ctrl);
mdbgv_3xc("%s\n", vctx, __func__);
framemgr = GET_FRAMEMGR(vctx);
switch (ctrl->id) {
case V4L2_CID_IS_G_COMPLETES:
ctrl->value = framemgr->queued_count[FS_COMPLETE];
break;
default:
err("unsupported ioctl(%d)\n", ctrl->id);
ret = -EINVAL;
break;
}
return ret;
}
static int fimc_is_3xc_video_g_ext_ctrl(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
return 0;
}
static int fimc_is_3xc_video_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!ctrl);
mdbgv_3xc("%s\n", vctx, __func__);
switch (ctrl->id) {
default:
ret = fimc_is_video_s_ctrl(file, vctx, ctrl);
if (ret) {
err("fimc_is_video_s_ctrl is fail(%d)", ret);
goto p_err;
}
break;
}
p_err:
return ret;
}
const struct v4l2_ioctl_ops fimc_is_3xc_video_ioctl_ops = {
.vidioc_querycap = fimc_is_3xc_video_querycap,
.vidioc_enum_fmt_vid_cap_mplane = fimc_is_3xc_video_enum_fmt_mplane,
.vidioc_g_fmt_vid_cap_mplane = fimc_is_3xc_video_get_format_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_is_3xc_video_set_format_mplane,
.vidioc_try_fmt_vid_cap_mplane = fimc_is_3xc_video_try_format_mplane,
.vidioc_cropcap = fimc_is_3xc_video_cropcap,
.vidioc_g_crop = fimc_is_3xc_video_get_crop,
.vidioc_s_crop = fimc_is_3xc_video_set_crop,
.vidioc_reqbufs = fimc_is_3xc_video_reqbufs,
.vidioc_querybuf = fimc_is_3xc_video_querybuf,
.vidioc_qbuf = fimc_is_3xc_video_qbuf,
.vidioc_dqbuf = fimc_is_3xc_video_dqbuf,
.vidioc_prepare_buf = fimc_is_3xc_video_prepare,
.vidioc_streamon = fimc_is_3xc_video_streamon,
.vidioc_streamoff = fimc_is_3xc_video_streamoff,
.vidioc_enum_input = fimc_is_3xc_video_enum_input,
.vidioc_g_input = fimc_is_3xc_video_g_input,
.vidioc_s_input = fimc_is_3xc_video_s_input,
.vidioc_g_ctrl = fimc_is_3xc_video_g_ctrl,
.vidioc_s_ctrl = fimc_is_3xc_video_s_ctrl,
.vidioc_g_ext_ctrls = fimc_is_3xc_video_g_ext_ctrl,
};
static int fimc_is_3xc_queue_setup(struct vb2_queue *vbq,
const struct v4l2_format *fmt,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
void *allocators[])
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_video *video;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
mdbgv_3xc("%s\n", vctx, __func__);
video = GET_VIDEO(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_setup(queue,
video->alloc_ctx,
num_planes,
sizes,
allocators);
if (ret)
merr("fimc_is_queue_setup is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xc_buffer_prepare(struct vb2_buffer *vb)
{
return fimc_is_queue_prepare(vb);
}
static inline void fimc_is_3xc_wait_prepare(struct vb2_queue *vbq)
{
fimc_is_queue_wait_prepare(vbq);
}
static inline void fimc_is_3xc_wait_finish(struct vb2_queue *vbq)
{
fimc_is_queue_wait_finish(vbq);
}
static int fimc_is_3xc_start_streaming(struct vb2_queue *vbq,
unsigned int count)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_3xc("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_start_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_start_streaming is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static void fimc_is_3xc_stop_streaming(struct vb2_queue *vbq)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_3xc("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_stop_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_stop_streaming is fail(%d)", device, ret);
return;
}
}
static void fimc_is_3xc_buffer_queue(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_3xc("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
subdev = &device->txc;
ret = fimc_is_queue_buffer_queue(queue, vb);
if (ret) {
merr("fimc_is_queue_buffer_queue is fail(%d)", device, ret);
return;
}
ret = fimc_is_subdev_buffer_queue(subdev, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_subdev_buffer_queue is fail(%d)", device, ret);
return;
}
}
static void fimc_is_3xc_buffer_finish(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_3xc("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
subdev = &device->txc;
ret = fimc_is_subdev_buffer_finish(subdev, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_subdev_buffer_finish is fail(%d)", device, ret);
return;
}
}
const struct vb2_ops fimc_is_3xc_qops = {
.queue_setup = fimc_is_3xc_queue_setup,
.buf_init = fimc_is_buffer_init,
.buf_prepare = fimc_is_3xc_buffer_prepare,
.buf_queue = fimc_is_3xc_buffer_queue,
.buf_finish = fimc_is_3xc_buffer_finish,
.wait_prepare = fimc_is_3xc_wait_prepare,
.wait_finish = fimc_is_3xc_wait_finish,
.start_streaming = fimc_is_3xc_start_streaming,
.stop_streaming = fimc_is_3xc_stop_streaming,
};

View file

@ -0,0 +1,740 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
const struct v4l2_file_operations fimc_is_3xp_video_fops;
const struct v4l2_ioctl_ops fimc_is_3xp_video_ioctl_ops;
const struct vb2_ops fimc_is_3xp_qops;
int fimc_is_30p_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_30p;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_3XP_NAME(0),
FIMC_IS_VIDEO_30P_NUM,
VFL_DIR_RX,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_3xp_video_fops,
&fimc_is_3xp_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
int fimc_is_31p_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_31p;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_3XP_NAME(1),
FIMC_IS_VIDEO_31P_NUM,
VFL_DIR_RX,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_3xp_video_fops,
&fimc_is_3xp_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
/*
* =============================================================================
* Video File Opertation
* =============================================================================
*/
static int fimc_is_3xp_video_open(struct file *file)
{
int ret = 0;
struct fimc_is_video *video;
struct fimc_is_video_ctx *vctx;
struct fimc_is_device_ischain *device;
struct fimc_is_resourcemgr *resourcemgr;
vctx = NULL;
device = NULL;
video = video_drvdata(file);
resourcemgr = video->resourcemgr;
if (!resourcemgr) {
err("resourcemgr is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_resource_open(resourcemgr, RESOURCE_TYPE_ISCHAIN, (void **)&device);
if (ret) {
err("fimc_is_resource_open is fail(%d)", ret);
goto p_err;
}
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
minfo("[3%dP:V] %s\n", device, GET_3XP_ID(video), __func__);
ret = open_vctx(file, video, &vctx, device->instance, FRAMEMGR_ID_3XP);
if (ret) {
merr("open_vctx is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_video_open(vctx,
device,
VIDEO_3XP_READY_BUFFERS,
video,
&fimc_is_3xp_qops,
&fimc_is_ischain_subdev_ops);
if (ret) {
merr("fimc_is_video_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
ret = fimc_is_ischain_subdev_open(device, vctx);
if (ret) {
merr("fimc_is_ischain_subdev_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3xp_video_close(struct file *file)
{
int ret = 0;
int refcount;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_video *video;
struct fimc_is_device_ischain *device;
BUG_ON(!file);
BUG_ON(!vctx);
BUG_ON(!GET_VIDEO(vctx));
BUG_ON(!GET_DEVICE(vctx));
video = GET_VIDEO(vctx);
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_subdev_close(device, vctx);
if (ret)
merr("fimc_is_ischain_subdev_close is fail(%d)", device, ret);
ret = fimc_is_video_close(vctx);
if (ret)
merr("fimc_is_video_close is fail(%d)", device, ret);
refcount = close_vctx(file, video, vctx);
if (refcount < 0)
merr("close_vctx is fail(%d)", device, refcount);
minfo("[3%dP:V] %s(%d,%d):%d\n", device, GET_3XP_ID(video), __func__, atomic_read(&device->open_cnt), refcount, ret);
return ret;
}
static unsigned int fimc_is_3xp_video_poll(struct file *file,
struct poll_table_struct *wait)
{
u32 ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_poll(file, vctx, wait);
if (ret)
merr("fimc_is_video_poll is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xp_video_mmap(struct file *file,
struct vm_area_struct *vma)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_mmap(file, vctx, vma);
if (ret)
merr("fimc_is_video_mmap is fail(%d)", vctx, ret);
return ret;
}
const struct v4l2_file_operations fimc_is_3xp_video_fops = {
.owner = THIS_MODULE,
.open = fimc_is_3xp_video_open,
.release = fimc_is_3xp_video_close,
.poll = fimc_is_3xp_video_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = fimc_is_3xp_video_mmap,
};
/*
* =============================================================================
* Video Ioctl Opertation
* =============================================================================
*/
static int fimc_is_3xp_video_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
/* Todo : add to query capability code */
return 0;
}
static int fimc_is_3xp_video_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xp_video_get_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xp_video_set_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!format);
mdbgv_3xp("%s\n", vctx, __func__);
ret = fimc_is_video_set_format_mplane(file, vctx, format);
if (ret) {
merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3xp_video_try_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xp_video_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cropcap)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xp_video_get_crop(struct file *file, void *fh,
struct v4l2_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xp_video_set_crop(struct file *file, void *fh,
const struct v4l2_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xp_video_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *leader, *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
mdbgv_3xp("%s(buffers : %d)\n", vctx, __func__, buf->count);
device = GET_DEVICE(vctx);
subdev = &device->txp;
leader = subdev->leader;
if (leader && test_bit(FIMC_IS_SUBDEV_START, &leader->state)) {
err("leader%d still running, subdev%d req is not applied", leader->id, subdev->id);
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_reqbufs(file, vctx, buf);
if (ret) {
merr("fimc_is_video_reqbufs is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3xp_video_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3xp("%s\n", vctx, __func__);
ret = fimc_is_video_querybuf(file, vctx, buf);
if (ret)
merr("fimc_is_video_querybuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xp_video_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
#ifdef DBG_STREAMING
mdbgv_3xp("%s(index : %d)\n", vctx, __func__, buf->index);
#endif
ret = CALL_VOPS(vctx, qbuf, buf);
if (ret)
merr("qbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xp_video_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
bool blocking = file->f_flags & O_NONBLOCK;
#ifdef DBG_STREAMING
mdbgv_3xp("%s\n", vctx, __func__);
#endif
ret = CALL_VOPS(vctx, dqbuf, buf, blocking);
if (ret)
merr("dqbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xp_video_prepare(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
BUG_ON(!buf);
BUG_ON(!vctx);
BUG_ON(!GET_FRAMEMGR(vctx));
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
#ifdef DBG_STREAMING
mdbgv_3xp("%s\n", vctx, __func__);
#endif
device = GET_DEVICE(vctx);
framemgr = GET_FRAMEMGR(vctx);
frame = &framemgr->frames[buf->index];
ret = fimc_is_video_prepare(file, vctx, buf);
if (ret) {
merr("fimc_is_video_prepare is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
minfo("[3%dP:V] %s(%d):%d\n", device, GET_3XP_ID(GET_VIDEO(vctx)), __func__, buf->index, ret);
return ret;
}
static int fimc_is_3xp_video_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3xp("%s\n", vctx, __func__);
ret = fimc_is_video_streamon(file, vctx, type);
if (ret)
merr("fimc_is_video_streamon is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xp_video_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_3xp("%s\n", vctx, __func__);
ret = fimc_is_video_streamoff(file, vctx, type);
if (ret)
merr("fimc_is_video_streamoff is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xp_video_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
/* Todo: add enum input control code */
return 0;
}
static int fimc_is_3xp_video_g_input(struct file *file, void *priv,
unsigned int *input)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_3xp_video_s_input(struct file *file, void *priv,
unsigned int input)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
ret = fimc_is_video_s_input(file, vctx);
if (ret) {
merr("fimc_is_video_s_input is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_3xp_video_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_framemgr *framemgr;
BUG_ON(!vctx);
BUG_ON(!ctrl);
mdbgv_3xp("%s\n", vctx, __func__);
framemgr = GET_FRAMEMGR(vctx);
switch (ctrl->id) {
case V4L2_CID_IS_G_COMPLETES:
ctrl->value = framemgr->queued_count[FS_COMPLETE];
break;
default:
err("unsupported ioctl(%d)\n", ctrl->id);
ret = -EINVAL;
break;
}
return ret;
}
static int fimc_is_3xp_video_g_ext_ctrl(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
return 0;
}
static int fimc_is_3xp_video_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!ctrl);
mdbgv_3xp("%s\n", vctx, __func__);
switch (ctrl->id) {
default:
ret = fimc_is_video_s_ctrl(file, vctx, ctrl);
if (ret) {
err("fimc_is_video_s_ctrl is fail(%d)", ret);
goto p_err;
}
break;
}
p_err:
return ret;
}
const struct v4l2_ioctl_ops fimc_is_3xp_video_ioctl_ops = {
.vidioc_querycap = fimc_is_3xp_video_querycap,
.vidioc_enum_fmt_vid_cap_mplane = fimc_is_3xp_video_enum_fmt_mplane,
.vidioc_g_fmt_vid_cap_mplane = fimc_is_3xp_video_get_format_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_is_3xp_video_set_format_mplane,
.vidioc_try_fmt_vid_cap_mplane = fimc_is_3xp_video_try_format_mplane,
.vidioc_cropcap = fimc_is_3xp_video_cropcap,
.vidioc_g_crop = fimc_is_3xp_video_get_crop,
.vidioc_s_crop = fimc_is_3xp_video_set_crop,
.vidioc_reqbufs = fimc_is_3xp_video_reqbufs,
.vidioc_querybuf = fimc_is_3xp_video_querybuf,
.vidioc_qbuf = fimc_is_3xp_video_qbuf,
.vidioc_dqbuf = fimc_is_3xp_video_dqbuf,
.vidioc_prepare_buf = fimc_is_3xp_video_prepare,
.vidioc_streamon = fimc_is_3xp_video_streamon,
.vidioc_streamoff = fimc_is_3xp_video_streamoff,
.vidioc_enum_input = fimc_is_3xp_video_enum_input,
.vidioc_g_input = fimc_is_3xp_video_g_input,
.vidioc_s_input = fimc_is_3xp_video_s_input,
.vidioc_g_ctrl = fimc_is_3xp_video_g_ctrl,
.vidioc_s_ctrl = fimc_is_3xp_video_s_ctrl,
.vidioc_g_ext_ctrls = fimc_is_3xp_video_g_ext_ctrl,
};
static int fimc_is_3xp_queue_setup(struct vb2_queue *vbq,
const struct v4l2_format *fmt,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
void *allocators[])
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_video *video;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
mdbgv_3xp("%s\n", vctx, __func__);
video = GET_VIDEO(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_setup(queue,
video->alloc_ctx,
num_planes,
sizes,
allocators);
if (ret)
merr("fimc_is_queue_setup is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_3xp_buffer_prepare(struct vb2_buffer *vb)
{
return fimc_is_queue_prepare(vb);
}
static inline void fimc_is_3xp_wait_prepare(struct vb2_queue *vbq)
{
fimc_is_queue_wait_prepare(vbq);
}
static inline void fimc_is_3xp_wait_finish(struct vb2_queue *vbq)
{
fimc_is_queue_wait_finish(vbq);
}
static int fimc_is_3xp_start_streaming(struct vb2_queue *vbq,
unsigned int count)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_3xp("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_start_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_start_streaming is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static void fimc_is_3xp_stop_streaming(struct vb2_queue *vbq)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_3xp("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_stop_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_stop_streaming is fail(%d)", device, ret);
return;
}
}
static void fimc_is_3xp_buffer_queue(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_3xp("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
subdev = &device->txp;
ret = fimc_is_queue_buffer_queue(queue, vb);
if (ret) {
merr("fimc_is_queue_buffer_queue is fail(%d)", device, ret);
return;
}
ret = fimc_is_subdev_buffer_queue(subdev, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_subdev_buffer_queue is fail(%d)", device, ret);
return;
}
}
static void fimc_is_3xp_buffer_finish(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_3xp("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
subdev = &device->txp;
ret = fimc_is_subdev_buffer_finish(subdev, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_subdev_buffer_finish is fail(%d)", device, ret);
return;
}
}
const struct vb2_ops fimc_is_3xp_qops = {
.queue_setup = fimc_is_3xp_queue_setup,
.buf_init = fimc_is_buffer_init,
.buf_prepare = fimc_is_3xp_buffer_prepare,
.buf_queue = fimc_is_3xp_buffer_queue,
.buf_finish = fimc_is_3xp_buffer_finish,
.wait_prepare = fimc_is_3xp_wait_prepare,
.wait_finish = fimc_is_3xp_wait_finish,
.start_streaming = fimc_is_3xp_start_streaming,
.stop_streaming = fimc_is_3xp_stop_streaming,
};

View file

@ -0,0 +1,676 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
const struct v4l2_file_operations fimc_is_dis_video_fops;
const struct v4l2_ioctl_ops fimc_is_dis_video_ioctl_ops;
const struct vb2_ops fimc_is_dis_qops;
int fimc_is_dis_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_vdo;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_DIS_NAME,
FIMC_IS_VIDEO_DIS_NUM,
VFL_DIR_M2M,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_dis_video_fops,
&fimc_is_dis_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
static int fimc_is_dis_video_open(struct file *file)
{
int ret = 0;
struct fimc_is_video *video;
struct fimc_is_video_ctx *vctx;
struct fimc_is_device_ischain *device;
struct fimc_is_resourcemgr *resourcemgr;
vctx = NULL;
device = NULL;
video = video_drvdata(file);
resourcemgr = video->resourcemgr;
if (!resourcemgr) {
err("resourcemgr is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_resource_open(resourcemgr, RESOURCE_TYPE_ISCHAIN, (void **)&device);
if (ret) {
err("fimc_is_resource_open is fail(%d)", ret);
goto p_err;
}
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
minfo("[DIS:V] %s\n", device, __func__);
ret = open_vctx(file, video, &vctx, device->instance, FRAMEMGR_ID_DIS);
if (ret) {
merr("open_vctx is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_video_open(vctx,
device,
VIDEO_DIS_READY_BUFFERS,
video,
&fimc_is_dis_qops,
&fimc_is_ischain_dis_ops);
if (ret) {
merr("fimc_is_video_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
ret = fimc_is_ischain_dis_open(device, vctx);
if (ret) {
merr("fimc_is_ischain_dis_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_dis_video_close(struct file *file)
{
int ret = 0;
int refcount;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_video *video;
struct fimc_is_device_ischain *device;
BUG_ON(!file);
BUG_ON(!vctx);
BUG_ON(!GET_VIDEO(vctx));
BUG_ON(!GET_DEVICE(vctx));
video = GET_VIDEO(vctx);
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_dis_close(device, vctx);
if (ret)
merr("fimc_is_ischain_dis_close is fail(%d)", device, ret);
ret = fimc_is_video_close(vctx);
if (ret)
merr("fimc_is_video_close is fail(%d)", device, ret);
refcount = close_vctx(file, video, vctx);
if (refcount < 0)
merr("close_vctx is fail(%d)", device, refcount);
minfo("[DIS:V] %s(%d,%d):%d\n", device, __func__, atomic_read(&device->open_cnt), refcount, ret);
return ret;
}
static unsigned int fimc_is_dis_video_poll(struct file *file,
struct poll_table_struct *wait)
{
u32 ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_poll(file, vctx, wait);
if (ret)
merr("fimc_is_video_poll is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_dis_video_mmap(struct file *file,
struct vm_area_struct *vma)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_mmap(file, vctx, vma);
if (ret)
merr("fimc_is_video_mmap is fail(%d)", vctx, ret);
return ret;
}
const struct v4l2_file_operations fimc_is_dis_video_fops = {
.owner = THIS_MODULE,
.open = fimc_is_dis_video_open,
.release = fimc_is_dis_video_close,
.poll = fimc_is_dis_video_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = fimc_is_dis_video_mmap,
};
static int fimc_is_dis_video_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
struct fimc_is_core *core = video_drvdata(file);
strncpy(cap->driver, core->pdev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, core->pdev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
cap->version = KERNEL_VERSION(1, 0, 0);
cap->capabilities = V4L2_CAP_STREAMING
| V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_VIDEO_CAPTURE_MPLANE;
return 0;
}
static int fimc_is_dis_video_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
/* Todo: add enum format control code */
return 0;
}
static int fimc_is_dis_video_get_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
/* Todo: add get format control code */
return 0;
}
static int fimc_is_dis_video_set_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!format);
mdbgv_dis("%s\n", vctx, __func__);
ret = fimc_is_video_set_format_mplane(file, vctx, format);
if (ret) {
merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_dis_video_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *buf)
{
int ret;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_dis("%s(buffers : %d)\n", vctx, __func__, buf->count);
ret = fimc_is_video_reqbufs(file, vctx, buf);
if (ret)
merr("fimc_is_video_reqbufs is fail(error %d)", vctx, ret);
return ret;
}
static int fimc_is_dis_video_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_dis("%s\n", vctx, __func__);
ret = fimc_is_video_querybuf(file, vctx, buf);
if (ret)
merr("fimc_is_video_querybuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_dis_video_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
#ifdef DBG_STREAMING
mdbgv_dis("%s\n", vctx, __func__);
#endif
queue = GET_QUEUE(vctx);
if (!test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) {
merr("stream off state, can NOT qbuf", vctx);
ret = -EINVAL;
goto p_err;
}
ret = CALL_VOPS(vctx, qbuf, buf);
if (ret)
merr("qbuf is fail(%d)", vctx, ret);
p_err:
return ret;
}
static int fimc_is_dis_video_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
bool blocking = file->f_flags & O_NONBLOCK;
#ifdef DBG_STREAMING
mdbgv_dis("%s\n", vctx, __func__);
#endif
ret = CALL_VOPS(vctx, dqbuf, buf, blocking);
if (ret)
merr("dqbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_dis_video_prepare(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
BUG_ON(!buf);
BUG_ON(!vctx);
BUG_ON(!GET_FRAMEMGR(vctx));
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
#ifdef DBG_STREAMING
mdbgv_dis("%s\n", vctx, __func__);
#endif
device = GET_DEVICE(vctx);
framemgr = GET_FRAMEMGR(vctx);
frame = &framemgr->frames[buf->index];
ret = fimc_is_video_prepare(file, vctx, buf);
if (ret) {
merr("fimc_is_video_prepare is fail(%d)", vctx, ret);
goto p_err;
}
if (!test_bit(FRAME_MEM_MAPPED, &frame->mem_state)) {
fimc_is_itf_map(device, GROUP_ID(device->group_dis.id), frame->dvaddr_shot, frame->shot_size);
set_bit(FRAME_MEM_MAPPED, &frame->mem_state);
}
p_err:
minfo("[DIS:V] %s(%d):%d\n", device, __func__, buf->index, ret);
return ret;
}
static int fimc_is_dis_video_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_dis("%s\n", vctx, __func__);
ret = fimc_is_video_streamon(file, vctx, type);
if (ret)
merr("fimc_is_dis_video_streamon is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_dis_video_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_dis("%s\n", vctx, __func__);
ret = fimc_is_video_streamoff(file, vctx, type);
if (ret)
merr("fimc_is_video_streamoff is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_dis_video_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
/* Todo : add to enum input control code */
return 0;
}
static int fimc_is_dis_video_g_input(struct file *file, void *priv,
unsigned int *input)
{
/* Todo: add get input control code */
return 0;
}
static int fimc_is_dis_video_s_input(struct file *file, void *priv,
unsigned int input)
{
int ret = 0;
u32 stream, module, vindex, intype, leader;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!vctx->device);
device = GET_DEVICE(vctx);
stream = (input & INPUT_STREAM_MASK) >> INPUT_STREAM_SHIFT;
module = (input & INPUT_MODULE_MASK) >> INPUT_MODULE_SHIFT;
vindex = (input & INPUT_VINDEX_MASK) >> INPUT_VINDEX_SHIFT;
intype = (input & INPUT_INTYPE_MASK) >> INPUT_INTYPE_SHIFT;
leader = (input & INPUT_LEADER_MASK) >> INPUT_LEADER_SHIFT;
mdbgv_dis("%s(input : %08X)[%d,%d,%d,%d,%d]\n", vctx, __func__, input,
stream, module, vindex, intype, leader);
ret = fimc_is_video_s_input(file, vctx);
if (ret) {
merr("fimc_is_video_s_input is fail(%d)", vctx, ret);
goto p_err;
}
ret = fimc_is_ischain_dis_s_input(device, stream, module, vindex, intype, leader);
if (ret) {
merr("fimc_is_ischain_isp_s_input is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_dis_video_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!ctrl);
mdbgv_dis("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
switch (ctrl->id) {
case V4L2_CID_IS_FORCE_DONE:
set_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &device->group_dis.state);
break;
default:
ret = fimc_is_video_s_ctrl(file, vctx, ctrl);
if (ret) {
err("fimc_is_video_s_ctrl is fail(%d)", ret);
goto p_err;
}
break;
}
p_err:
return ret;
}
static int fimc_is_dis_video_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
/* Todo: add get control code */
return 0;
}
static int fimc_is_dis_video_g_ext_ctrl(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
/* Todo: add get extra control code */
return 0;
}
const struct v4l2_ioctl_ops fimc_is_dis_video_ioctl_ops = {
.vidioc_querycap = fimc_is_dis_video_querycap,
.vidioc_enum_fmt_vid_out_mplane = fimc_is_dis_video_enum_fmt_mplane,
.vidioc_g_fmt_vid_out_mplane = fimc_is_dis_video_get_format_mplane,
.vidioc_s_fmt_vid_out_mplane = fimc_is_dis_video_set_format_mplane,
.vidioc_reqbufs = fimc_is_dis_video_reqbufs,
.vidioc_querybuf = fimc_is_dis_video_querybuf,
.vidioc_qbuf = fimc_is_dis_video_qbuf,
.vidioc_dqbuf = fimc_is_dis_video_dqbuf,
.vidioc_prepare_buf = fimc_is_dis_video_prepare,
.vidioc_streamon = fimc_is_dis_video_streamon,
.vidioc_streamoff = fimc_is_dis_video_streamoff,
.vidioc_enum_input = fimc_is_dis_video_enum_input,
.vidioc_g_input = fimc_is_dis_video_g_input,
.vidioc_s_input = fimc_is_dis_video_s_input,
.vidioc_s_ctrl = fimc_is_dis_video_s_ctrl,
.vidioc_g_ctrl = fimc_is_dis_video_g_ctrl,
.vidioc_g_ext_ctrls = fimc_is_dis_video_g_ext_ctrl,
};
static int fimc_is_dis_queue_setup(struct vb2_queue *vbq,
const struct v4l2_format *fmt,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[],
void *allocators[])
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_video *video;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
mdbgv_isp("%s\n", vctx, __func__);
video = GET_VIDEO(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_setup(queue,
video->alloc_ctx,
num_planes,
sizes,
allocators);
if (ret)
merr("fimc_is_queue_setup is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_dis_buffer_prepare(struct vb2_buffer *vb)
{
return fimc_is_queue_prepare(vb);
}
static inline void fimc_is_dis_wait_prepare(struct vb2_queue *vbq)
{
fimc_is_queue_wait_prepare(vbq);
}
static inline void fimc_is_dis_wait_finish(struct vb2_queue *vbq)
{
fimc_is_queue_wait_finish(vbq);
}
static int fimc_is_dis_start_streaming(struct vb2_queue *vbq,
unsigned int count)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_dis("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_start_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_start_streaming is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static void fimc_is_dis_stop_streaming(struct vb2_queue *vbq)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_dis("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_stop_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_stop_streaming is fail(%d)", device, ret);
return;
}
}
static void fimc_is_dis_buffer_queue(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_dis("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_buffer_queue(queue, vb);
if (ret) {
merr("fimc_is_queue_buffer_queue is fail(%d)", device, ret);
return;
}
ret = fimc_is_ischain_dis_buffer_queue(device, queue, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_ischain_dis_buffer_queue is fail(%d)", device, ret);
return;
}
}
static void fimc_is_dis_buffer_finish(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_device_ischain *device = vctx->device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_dis("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_dis_buffer_finish(device, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_ischain_dis_buffer_finish is fail(%d)", device, ret);
return;
}
}
const struct vb2_ops fimc_is_dis_qops = {
.queue_setup = fimc_is_dis_queue_setup,
.buf_init = fimc_is_buffer_init,
.buf_prepare = fimc_is_dis_buffer_prepare,
.buf_queue = fimc_is_dis_buffer_queue,
.buf_finish = fimc_is_dis_buffer_finish,
.wait_prepare = fimc_is_dis_wait_prepare,
.wait_finish = fimc_is_dis_wait_finish,
.start_streaming = fimc_is_dis_start_streaming,
.stop_streaming = fimc_is_dis_stop_streaming,
};

View file

@ -0,0 +1,812 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/pm_qos.h>
#include <linux/bug.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
const struct v4l2_file_operations fimc_is_ixs_video_fops;
const struct v4l2_ioctl_ops fimc_is_ixs_video_ioctl_ops;
const struct vb2_ops fimc_is_ixs_qops;
int fimc_is_i0s_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_i0s;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_IXS_NAME(0),
FIMC_IS_VIDEO_I0S_NUM,
VFL_DIR_M2M,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_ixs_video_fops,
&fimc_is_ixs_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
int fimc_is_i1s_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_i1s;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_IXS_NAME(1),
FIMC_IS_VIDEO_I1S_NUM,
VFL_DIR_M2M,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_ixs_video_fops,
&fimc_is_ixs_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
/*
* =============================================================================
* Video File Opertation
* =============================================================================
*/
static int fimc_is_ixs_video_open(struct file *file)
{
int ret = 0;
struct fimc_is_video *video;
struct fimc_is_video_ctx *vctx;
struct fimc_is_device_ischain *device;
struct fimc_is_resourcemgr *resourcemgr;
vctx = NULL;
device = NULL;
video = video_drvdata(file);
resourcemgr = video->resourcemgr;
if (!resourcemgr) {
err("resourcemgr is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_resource_open(resourcemgr, RESOURCE_TYPE_ISCHAIN, (void **)&device);
if (ret) {
err("fimc_is_resource_open is fail(%d)", ret);
goto p_err;
}
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
minfo("[I%dS:V] %s\n", device, GET_IXS_ID(video), __func__);
ret = open_vctx(file, video, &vctx, device->instance, FRAMEMGR_ID_IXS);
if (ret) {
merr("open_vctx is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_video_open(vctx,
device,
VIDEO_IXS_READY_BUFFERS,
video,
&fimc_is_ixs_qops,
&fimc_is_ischain_isp_ops);
if (ret) {
merr("fimc_is_video_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
ret = fimc_is_ischain_isp_open(device, vctx);
if (ret) {
merr("fimc_is_ischain_isp_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_ixs_video_close(struct file *file)
{
int ret = 0;
int refcount;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_video *video;
struct fimc_is_device_ischain *device;
BUG_ON(!file);
BUG_ON(!vctx);
BUG_ON(!GET_VIDEO(vctx));
BUG_ON(!GET_DEVICE(vctx));
video = GET_VIDEO(vctx);
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_isp_close(device, vctx);
if (ret)
merr("fimc_is_ischain_isp_close is fail(%d)", device, ret);
ret = fimc_is_video_close(vctx);
if (ret)
merr("fimc_is_video_close is fail(%d)", device, ret);
refcount = close_vctx(file, video, vctx);
if (refcount < 0)
merr("close_vctx is fail(%d)", device, refcount);
minfo("[I%dS:V] %s(%d,%d):%d\n", device, GET_IXS_ID(video), __func__, atomic_read(&device->open_cnt), refcount, ret);
return ret;
}
static unsigned int fimc_is_ixs_video_poll(struct file *file,
struct poll_table_struct *wait)
{
u32 ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_poll(file, vctx, wait);
if (ret)
merr("fimc_is_video_poll is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixs_video_mmap(struct file *file,
struct vm_area_struct *vma)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_mmap(file, vctx, vma);
if (ret)
merr("fimc_is_video_mmap is fail(%d)", vctx, ret);
return ret;
}
const struct v4l2_file_operations fimc_is_ixs_video_fops = {
.owner = THIS_MODULE,
.open = fimc_is_ixs_video_open,
.release = fimc_is_ixs_video_close,
.poll = fimc_is_ixs_video_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = fimc_is_ixs_video_mmap,
};
/*
* =============================================================================
* Video Ioctl Opertation
* =============================================================================
*/
static int fimc_is_ixs_video_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
struct fimc_is_core *isp = video_drvdata(file);
strncpy(cap->driver, isp->pdev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, isp->pdev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
cap->version = KERNEL_VERSION(1, 0, 0);
cap->capabilities = V4L2_CAP_STREAMING
| V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_VIDEO_CAPTURE_MPLANE;
return 0;
}
static int fimc_is_ixs_video_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
mdbgv_isp("%s\n", vctx, __func__);
return 0;
}
static int fimc_is_ixs_video_get_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
mdbgv_isp("%s\n", vctx, __func__);
return 0;
}
static int fimc_is_ixs_video_set_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!format);
mdbgv_isp("%s\n", vctx, __func__);
ret = fimc_is_video_set_format_mplane(file, vctx, format);
if (ret) {
merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_ixs_video_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cropcap)
{
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
mdbgv_isp("%s\n", vctx, __func__);
return 0;
}
static int fimc_is_ixs_video_get_crop(struct file *file, void *fh,
struct v4l2_crop *crop)
{
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
mdbgv_isp("%s\n", vctx, __func__);
return 0;
}
static int fimc_is_ixs_video_set_crop(struct file *file, void *fh,
const struct v4l2_crop *crop)
{
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
mdbgv_isp("%s\n", vctx, __func__);
return 0;
}
static int fimc_is_ixs_video_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
mdbgv_isp("%s(buffers : %d)\n", vctx, __func__, buf->count);
ret = fimc_is_video_reqbufs(file, vctx, buf);
if (ret)
merr("fimc_is_video_reqbufs is fail(error %d)", vctx, ret);
return ret;
}
static int fimc_is_ixs_video_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_isp("%s\n", vctx, __func__);
ret = fimc_is_video_querybuf(file, vctx, buf);
if (ret)
merr("fimc_is_video_querybuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixs_video_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
#ifdef DBG_STREAMING
mdbgv_isp("%s(index : %d)\n", vctx, __func__, buf->index);
#endif
queue = GET_QUEUE(vctx);
if (!test_bit(FIMC_IS_QUEUE_STREAM_ON, &queue->state)) {
merr("stream off state, can NOT qbuf", vctx);
ret = -EINVAL;
goto p_err;
}
ret = CALL_VOPS(vctx, qbuf, buf);
if (ret)
merr("qbuf is fail(%d)", vctx, ret);
p_err:
return ret;
}
static int fimc_is_ixs_video_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
bool blocking = file->f_flags & O_NONBLOCK;
#ifdef DBG_STREAMING
mdbgv_isp("%s\n", vctx, __func__);
#endif
ret = CALL_VOPS(vctx, dqbuf, buf, blocking);
if (ret)
merr("dqbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixs_video_prepare(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
BUG_ON(!buf);
BUG_ON(!vctx);
BUG_ON(!GET_FRAMEMGR(vctx));
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
#ifdef DBG_STREAMING
mdbgv_isp("%s\n", vctx, __func__);
#endif
device = GET_DEVICE(vctx);
framemgr = GET_FRAMEMGR(vctx);
frame = &framemgr->frames[buf->index];
ret = fimc_is_video_prepare(file, vctx, buf);
if (ret) {
merr("fimc_is_video_prepare is fail(%d)", vctx, ret);
goto p_err;
}
if (!test_bit(FRAME_MEM_MAPPED, &frame->mem_state)) {
fimc_is_itf_map(device, GROUP_ID(device->group_isp.id), frame->dvaddr_shot, frame->shot_size);
set_bit(FRAME_MEM_MAPPED, &frame->mem_state);
}
p_err:
minfo("[I%dS:V] %s(%d):%d\n", device, GET_IXS_ID(GET_VIDEO(vctx)), __func__, buf->index, ret);
return ret;
}
static int fimc_is_ixs_video_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_isp("%s\n", vctx, __func__);
ret = fimc_is_video_streamon(file, vctx, type);
if (ret)
merr("fimc_is_video_streamon is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixs_video_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_isp("%s\n", vctx, __func__);
ret = fimc_is_video_streamoff(file, vctx, type);
if (ret)
merr("fimc_is_video_streamoff is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixs_video_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
/* Todo : add to enum input control code */
return 0;
}
static int fimc_is_ixs_video_g_input(struct file *file, void *priv,
unsigned int *input)
{
/* Todo : add to get input control code */
return 0;
}
static int fimc_is_ixs_video_s_input(struct file *file, void *priv,
unsigned int input)
{
int ret = 0;
u32 stream, module, vindex, intype, leader;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!vctx->device);
device = GET_DEVICE(vctx);
stream = (input & INPUT_STREAM_MASK) >> INPUT_STREAM_SHIFT;
module = (input & INPUT_MODULE_MASK) >> INPUT_MODULE_SHIFT;
vindex = (input & INPUT_VINDEX_MASK) >> INPUT_VINDEX_SHIFT;
intype = (input & INPUT_INTYPE_MASK) >> INPUT_INTYPE_SHIFT;
leader = (input & INPUT_LEADER_MASK) >> INPUT_LEADER_SHIFT;
mdbgv_isp("%s(input : %08X)[%d,%d,%d,%d,%d]\n", vctx, __func__, input,
stream, module, vindex, intype, leader);
ret = fimc_is_video_s_input(file, vctx);
if (ret) {
merr("fimc_is_video_s_input is fail(%d)", vctx, ret);
goto p_err;
}
ret = fimc_is_ischain_isp_s_input(device, stream, module, vindex, intype, leader);
if (ret) {
merr("fimc_is_ischain_isp_s_input is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_ixs_video_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_video *video;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
device = GET_DEVICE(vctx);
video = GET_VIDEO(vctx);
mdbgv_isp("%s\n", vctx, __func__);
switch (ctrl->id) {
case V4L2_CID_IS_G_CAPABILITY:
ret = fimc_is_ischain_g_capability(device, ctrl->value);
break;
case V4L2_CID_IS_FORCE_DONE:
set_bit(FIMC_IS_GROUP_REQUEST_FSTOP, &device->group_isp.state);
break;
case V4L2_CID_IS_DVFS_LOCK:
pm_qos_add_request(&device->user_qos, PM_QOS_DEVICE_THROUGHPUT, ctrl->value);
mdbgv_isp("V4L2_CID_IS_DVFS_LOCK : %d\n", vctx, ctrl->value);
break;
case V4L2_CID_IS_DVFS_UNLOCK:
pm_qos_remove_request(&device->user_qos);
mdbgv_isp("V4L2_CID_IS_DVFS_UNLOCK : %d\n", vctx, ctrl->value);
break;
default:
ret = fimc_is_video_s_ctrl(file, vctx, ctrl);
if (ret) {
err("fimc_is_video_s_ctrl is fail(%d)", ret);
goto p_err;
}
break;
}
p_err:
return ret;
}
static int fimc_is_ixs_video_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_isp("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
switch (ctrl->id) {
default:
err("unsupported ioctl(%d)\n", ctrl->id);
ret = -EINVAL;
break;
}
return ret;
}
static int fimc_is_ixs_video_g_ext_ctrl(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
int ret = 0;
struct v4l2_ext_control *ctrl;
ctrl = ctrls->controls;
switch (ctrl->id) {
default:
err("unsupported ioctl(%d)\n", ctrl->id);
ret = -EINVAL;
break;
}
return ret;
}
const struct v4l2_ioctl_ops fimc_is_ixs_video_ioctl_ops = {
.vidioc_querycap = fimc_is_ixs_video_querycap,
.vidioc_enum_fmt_vid_out_mplane = fimc_is_ixs_video_enum_fmt_mplane,
.vidioc_g_fmt_vid_out_mplane = fimc_is_ixs_video_get_format_mplane,
.vidioc_s_fmt_vid_out_mplane = fimc_is_ixs_video_set_format_mplane,
.vidioc_cropcap = fimc_is_ixs_video_cropcap,
.vidioc_g_crop = fimc_is_ixs_video_get_crop,
.vidioc_s_crop = fimc_is_ixs_video_set_crop,
.vidioc_reqbufs = fimc_is_ixs_video_reqbufs,
.vidioc_querybuf = fimc_is_ixs_video_querybuf,
.vidioc_qbuf = fimc_is_ixs_video_qbuf,
.vidioc_dqbuf = fimc_is_ixs_video_dqbuf,
.vidioc_prepare_buf = fimc_is_ixs_video_prepare,
.vidioc_streamon = fimc_is_ixs_video_streamon,
.vidioc_streamoff = fimc_is_ixs_video_streamoff,
.vidioc_enum_input = fimc_is_ixs_video_enum_input,
.vidioc_g_input = fimc_is_ixs_video_g_input,
.vidioc_s_input = fimc_is_ixs_video_s_input,
.vidioc_s_ctrl = fimc_is_ixs_video_s_ctrl,
.vidioc_g_ctrl = fimc_is_ixs_video_g_ctrl,
.vidioc_g_ext_ctrls = fimc_is_ixs_video_g_ext_ctrl,
};
static int fimc_is_ixs_queue_setup(struct vb2_queue *vbq,
const struct v4l2_format *fmt,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[],
void *allocators[])
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_video *video;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
mdbgv_isp("%s\n", vctx, __func__);
video = GET_VIDEO(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_setup(queue,
video->alloc_ctx,
num_planes,
sizes,
allocators);
if (ret)
merr("fimc_is_queue_setup is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixs_buffer_prepare(struct vb2_buffer *vb)
{
return fimc_is_queue_prepare(vb);
}
static inline void fimc_is_ixs_wait_prepare(struct vb2_queue *vbq)
{
fimc_is_queue_wait_prepare(vbq);
}
static inline void fimc_is_ixs_wait_finish(struct vb2_queue *vbq)
{
fimc_is_queue_wait_finish(vbq);
}
static int fimc_is_ixs_start_streaming(struct vb2_queue *vbq,
unsigned int count)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_isp("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_start_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_start_streaming is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static void fimc_is_ixs_stop_streaming(struct vb2_queue *vbq)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_isp("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_stop_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_stop_streaming is fail(%d)", device, ret);
return;
}
}
static void fimc_is_ixs_buffer_queue(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_isp("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_buffer_queue(queue, vb);
if (ret) {
merr("fimc_is_queue_buffer_queue is fail(%d)", device, ret);
return;
}
ret = fimc_is_ischain_isp_buffer_queue(device, queue, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_ischain_isp_buffer_queue is fail(%d)", device, ret);
return;
}
}
static void fimc_is_ixs_buffer_finish(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_isp("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_isp_buffer_finish(device, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_ischain_isp_buffer_finish is fail(%d)", device, ret);
return;
}
}
const struct vb2_ops fimc_is_ixs_qops = {
.queue_setup = fimc_is_ixs_queue_setup,
.buf_init = fimc_is_buffer_init,
.buf_prepare = fimc_is_ixs_buffer_prepare,
.buf_queue = fimc_is_ixs_buffer_queue,
.buf_finish = fimc_is_ixs_buffer_finish,
.wait_prepare = fimc_is_ixs_wait_prepare,
.wait_finish = fimc_is_ixs_wait_finish,
.start_streaming = fimc_is_ixs_start_streaming,
.stop_streaming = fimc_is_ixs_stop_streaming,
};

View file

@ -0,0 +1,740 @@
/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
const struct v4l2_file_operations fimc_is_ixc_video_fops;
const struct v4l2_ioctl_ops fimc_is_ixc_video_ioctl_ops;
const struct vb2_ops fimc_is_ixc_qops;
int fimc_is_i0c_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_i0c;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_IXC_NAME(0),
FIMC_IS_VIDEO_I0C_NUM,
VFL_DIR_RX,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_ixc_video_fops,
&fimc_is_ixc_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
int fimc_is_i1c_video_probe(void *data)
{
int ret = 0;
struct fimc_is_core *core;
struct fimc_is_video *video;
BUG_ON(!data);
core = (struct fimc_is_core *)data;
video = &core->video_i1c;
video->resourcemgr = &core->resourcemgr;
if (!core->pdev) {
probe_err("pdev is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_probe(video,
FIMC_IS_VIDEO_IXC_NAME(1),
FIMC_IS_VIDEO_I1C_NUM,
VFL_DIR_RX,
&core->resourcemgr.mem,
&core->v4l2_dev,
&fimc_is_ixc_video_fops,
&fimc_is_ixc_video_ioctl_ops);
if (ret)
dev_err(&core->pdev->dev, "%s is fail(%d)\n", __func__, ret);
p_err:
return ret;
}
/*
* =============================================================================
* Video File Opertation
* =============================================================================
*/
static int fimc_is_ixc_video_open(struct file *file)
{
int ret = 0;
struct fimc_is_video *video;
struct fimc_is_video_ctx *vctx;
struct fimc_is_device_ischain *device;
struct fimc_is_resourcemgr *resourcemgr;
vctx = NULL;
device = NULL;
video = video_drvdata(file);
resourcemgr = video->resourcemgr;
if (!resourcemgr) {
err("resourcemgr is NULL");
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_resource_open(resourcemgr, RESOURCE_TYPE_ISCHAIN, (void **)&device);
if (ret) {
err("fimc_is_resource_open is fail(%d)", ret);
goto p_err;
}
if (!device) {
err("device is NULL");
ret = -EINVAL;
goto p_err;
}
minfo("[I%dC:V] %s\n", device, GET_IXC_ID(video), __func__);
ret = open_vctx(file, video, &vctx, device->instance, FRAMEMGR_ID_IXC);
if (ret) {
merr("open_vctx is fail(%d)", device, ret);
goto p_err;
}
ret = fimc_is_video_open(vctx,
device,
VIDEO_IXC_READY_BUFFERS,
video,
&fimc_is_ixc_qops,
&fimc_is_ischain_subdev_ops);
if (ret) {
merr("fimc_is_video_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
ret = fimc_is_ischain_subdev_open(device, vctx);
if (ret) {
merr("fimc_is_ischain_subdev_open is fail(%d)", device, ret);
close_vctx(file, video, vctx);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_ixc_video_close(struct file *file)
{
int ret = 0;
int refcount;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_video *video;
struct fimc_is_device_ischain *device;
BUG_ON(!file);
BUG_ON(!vctx);
BUG_ON(!GET_VIDEO(vctx));
BUG_ON(!GET_DEVICE(vctx));
video = GET_VIDEO(vctx);
device = GET_DEVICE(vctx);
ret = fimc_is_ischain_subdev_close(device, vctx);
if (ret)
merr("fimc_is_ischain_subdev_close is fail(%d)", device, ret);
ret = fimc_is_video_close(vctx);
if (ret)
merr("fimc_is_video_close is fail(%d)", device, ret);
refcount = close_vctx(file, video, vctx);
if (refcount < 0)
merr("close_vctx is fail(%d)", device, refcount);
minfo("[I%dC:V] %s(%d,%d):%d\n", device, GET_IXC_ID(video), __func__, atomic_read(&device->open_cnt), refcount, ret);
return ret;
}
static unsigned int fimc_is_ixc_video_poll(struct file *file,
struct poll_table_struct *wait)
{
u32 ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_poll(file, vctx, wait);
if (ret)
merr("fimc_is_video_poll is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixc_video_mmap(struct file *file,
struct vm_area_struct *vma)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
ret = fimc_is_video_mmap(file, vctx, vma);
if (ret)
merr("fimc_is_video_mmap is fail(%d)", vctx, ret);
return ret;
}
const struct v4l2_file_operations fimc_is_ixc_video_fops = {
.owner = THIS_MODULE,
.open = fimc_is_ixc_video_open,
.release = fimc_is_ixc_video_close,
.poll = fimc_is_ixc_video_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = fimc_is_ixc_video_mmap,
};
/*
* =============================================================================
* Video Ioctl Opertation
* =============================================================================
*/
static int fimc_is_ixc_video_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
/* Todo : add to query capability code */
return 0;
}
static int fimc_is_ixc_video_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_ixc_video_get_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_ixc_video_set_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!format);
mdbgv_ixc("%s\n", vctx, __func__);
ret = fimc_is_video_set_format_mplane(file, vctx, format);
if (ret) {
merr("fimc_is_video_set_format_mplane is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_ixc_video_try_format_mplane(struct file *file, void *fh,
struct v4l2_format *format)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_ixc_video_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cropcap)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_ixc_video_get_crop(struct file *file, void *fh,
struct v4l2_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_ixc_video_set_crop(struct file *file, void *fh,
const struct v4l2_crop *crop)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_ixc_video_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *leader, *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
mdbgv_ixc("%s(buffers : %d)\n", vctx, __func__, buf->count);
device = GET_DEVICE(vctx);
subdev = &device->ixc;
leader = subdev->leader;
if (leader && test_bit(FIMC_IS_SUBDEV_START, &leader->state)) {
err("leader%d still running, subdev%d req is not applied", leader->id, subdev->id);
ret = -EINVAL;
goto p_err;
}
ret = fimc_is_video_reqbufs(file, vctx, buf);
if (ret) {
merr("fimc_is_video_reqbufs is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_ixc_video_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_ixc("%s\n", vctx, __func__);
ret = fimc_is_video_querybuf(file, vctx, buf);
if (ret)
merr("fimc_is_video_querybuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixc_video_qbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
#ifdef DBG_STREAMING
mdbgv_ixc("%s(index : %d)\n", vctx, __func__, buf->index);
#endif
ret = CALL_VOPS(vctx, qbuf, buf);
if (ret)
merr("qbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixc_video_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
bool blocking = file->f_flags & O_NONBLOCK;
#ifdef DBG_STREAMING
mdbgv_ixc("%s\n", vctx, __func__);
#endif
ret = CALL_VOPS(vctx, dqbuf, buf, blocking);
if (ret)
merr("dqbuf is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixc_video_prepare(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_device_ischain *device;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
BUG_ON(!buf);
BUG_ON(!vctx);
BUG_ON(!GET_FRAMEMGR(vctx));
BUG_ON(!GET_DEVICE(vctx));
BUG_ON(!GET_VIDEO(vctx));
#ifdef DBG_STREAMING
mdbgv_ixc("%s\n", vctx, __func__);
#endif
device = GET_DEVICE(vctx);
framemgr = GET_FRAMEMGR(vctx);
frame = &framemgr->frames[buf->index];
ret = fimc_is_video_prepare(file, vctx, buf);
if (ret) {
merr("fimc_is_video_prepare is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
minfo("[I%dC:V] %s(%d):%d\n", device, GET_IXC_ID(GET_VIDEO(vctx)), __func__, buf->index, ret);
return ret;
}
static int fimc_is_ixc_video_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_ixc("%s\n", vctx, __func__);
ret = fimc_is_video_streamon(file, vctx, type);
if (ret)
merr("fimc_is_video_streamon is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixc_video_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
mdbgv_ixc("%s\n", vctx, __func__);
ret = fimc_is_video_streamoff(file, vctx, type);
if (ret)
merr("fimc_is_video_streamoff is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixc_video_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
/* Todo: add enum input control code */
return 0;
}
static int fimc_is_ixc_video_g_input(struct file *file, void *priv,
unsigned int *input)
{
dbg("%s\n", __func__);
return 0;
}
static int fimc_is_ixc_video_s_input(struct file *file, void *priv,
unsigned int input)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
ret = fimc_is_video_s_input(file, vctx);
if (ret) {
merr("fimc_is_video_s_input is fail(%d)", vctx, ret);
goto p_err;
}
p_err:
return ret;
}
static int fimc_is_ixc_video_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
struct fimc_is_framemgr *framemgr;
BUG_ON(!vctx);
BUG_ON(!ctrl);
mdbgv_ixc("%s\n", vctx, __func__);
framemgr = GET_FRAMEMGR(vctx);
switch (ctrl->id) {
case V4L2_CID_IS_G_COMPLETES:
ctrl->value = framemgr->queued_count[FS_COMPLETE];
break;
default:
err("unsupported ioctl(%d)\n", ctrl->id);
ret = -EINVAL;
break;
}
return ret;
}
static int fimc_is_ixc_video_g_ext_ctrl(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
return 0;
}
static int fimc_is_ixc_video_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = file->private_data;
BUG_ON(!vctx);
BUG_ON(!ctrl);
mdbgv_ixc("%s\n", vctx, __func__);
switch (ctrl->id) {
default:
ret = fimc_is_video_s_ctrl(file, vctx, ctrl);
if (ret) {
err("fimc_is_video_s_ctrl is fail(%d)", ret);
goto p_err;
}
break;
}
p_err:
return ret;
}
const struct v4l2_ioctl_ops fimc_is_ixc_video_ioctl_ops = {
.vidioc_querycap = fimc_is_ixc_video_querycap,
.vidioc_enum_fmt_vid_cap_mplane = fimc_is_ixc_video_enum_fmt_mplane,
.vidioc_g_fmt_vid_cap_mplane = fimc_is_ixc_video_get_format_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_is_ixc_video_set_format_mplane,
.vidioc_try_fmt_vid_cap_mplane = fimc_is_ixc_video_try_format_mplane,
.vidioc_cropcap = fimc_is_ixc_video_cropcap,
.vidioc_g_crop = fimc_is_ixc_video_get_crop,
.vidioc_s_crop = fimc_is_ixc_video_set_crop,
.vidioc_reqbufs = fimc_is_ixc_video_reqbufs,
.vidioc_querybuf = fimc_is_ixc_video_querybuf,
.vidioc_qbuf = fimc_is_ixc_video_qbuf,
.vidioc_dqbuf = fimc_is_ixc_video_dqbuf,
.vidioc_prepare_buf = fimc_is_ixc_video_prepare,
.vidioc_streamon = fimc_is_ixc_video_streamon,
.vidioc_streamoff = fimc_is_ixc_video_streamoff,
.vidioc_enum_input = fimc_is_ixc_video_enum_input,
.vidioc_g_input = fimc_is_ixc_video_g_input,
.vidioc_s_input = fimc_is_ixc_video_s_input,
.vidioc_g_ctrl = fimc_is_ixc_video_g_ctrl,
.vidioc_s_ctrl = fimc_is_ixc_video_s_ctrl,
.vidioc_g_ext_ctrls = fimc_is_ixc_video_g_ext_ctrl,
};
static int fimc_is_ixc_queue_setup(struct vb2_queue *vbq,
const struct v4l2_format *fmt,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
void *allocators[])
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_video *video;
struct fimc_is_queue *queue;
BUG_ON(!vctx);
BUG_ON(!vctx->video);
mdbgv_ixc("%s\n", vctx, __func__);
video = GET_VIDEO(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_setup(queue,
video->alloc_ctx,
num_planes,
sizes,
allocators);
if (ret)
merr("fimc_is_queue_setup is fail(%d)", vctx, ret);
return ret;
}
static int fimc_is_ixc_buffer_prepare(struct vb2_buffer *vb)
{
return fimc_is_queue_prepare(vb);
}
static inline void fimc_is_ixc_wait_prepare(struct vb2_queue *vbq)
{
fimc_is_queue_wait_prepare(vbq);
}
static inline void fimc_is_ixc_wait_finish(struct vb2_queue *vbq)
{
fimc_is_queue_wait_finish(vbq);
}
static int fimc_is_ixc_start_streaming(struct vb2_queue *vbq,
unsigned int count)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_ixc("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_start_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_start_streaming is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
static void fimc_is_ixc_stop_streaming(struct vb2_queue *vbq)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vbq->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
mdbgv_ixc("%s\n", vctx, __func__);
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
ret = fimc_is_queue_stop_streaming(queue, device);
if (ret) {
merr("fimc_is_queue_stop_streaming is fail(%d)", device, ret);
return;
}
}
static void fimc_is_ixc_buffer_queue(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_queue *queue;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_ixc("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
queue = GET_QUEUE(vctx);
subdev = &device->ixc;
ret = fimc_is_queue_buffer_queue(queue, vb);
if (ret) {
merr("fimc_is_queue_buffer_queue is fail(%d)", device, ret);
return;
}
ret = fimc_is_subdev_buffer_queue(subdev, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_subdev_buffer_queue is fail(%d)", device, ret);
return;
}
}
static void fimc_is_ixc_buffer_finish(struct vb2_buffer *vb)
{
int ret = 0;
struct fimc_is_video_ctx *vctx = vb->vb2_queue->drv_priv;
struct fimc_is_device_ischain *device;
struct fimc_is_subdev *subdev;
BUG_ON(!vctx);
BUG_ON(!GET_DEVICE(vctx));
#ifdef DBG_STREAMING
mdbgv_ixc("%s(%d)\n", vctx, __func__, vb->v4l2_buf.index);
#endif
device = GET_DEVICE(vctx);
subdev = &device->ixc;
ret = fimc_is_subdev_buffer_finish(subdev, vb->v4l2_buf.index);
if (ret) {
merr("fimc_is_subdev_buffer_finish is fail(%d)", device, ret);
return;
}
}
const struct vb2_ops fimc_is_ixc_qops = {
.queue_setup = fimc_is_ixc_queue_setup,
.buf_init = fimc_is_buffer_init,
.buf_prepare = fimc_is_ixc_buffer_prepare,
.buf_queue = fimc_is_ixc_buffer_queue,
.buf_finish = fimc_is_ixc_buffer_finish,
.wait_prepare = fimc_is_ixc_wait_prepare,
.wait_finish = fimc_is_ixc_wait_finish,
.start_streaming = fimc_is_ixc_start_streaming,
.stop_streaming = fimc_is_ixc_stop_streaming,
};

Some files were not shown because too many files have changed in this diff Show more